签入版本

This commit is contained in:
小蚂蚁云
2026-10-25 17:24:27 +08:00
parent 48911cc08f
commit fdbeae092a
1010 changed files with 30682 additions and 95274 deletions
+39 -57
View File
@@ -1,77 +1,59 @@
# 5.16 异常与响应处理
# 5.16 异常与统一响应
:::tip 概述
ExceptionHandle 将所有异常统一渲染为 JSON 响应,保证前端始终拿到结构一致的错误信息。
所有响应(成功、业务失败、校验失败、未认证、panic)都经 Response 中间件输出统一 JSON 信封,**绝不出现 HTML 错误页**。错误分三类:apperr 业务错误、普通 error、panic。
:::
## 响应结构
## 信封结构
```json
// 成功
{"code": 0, "ok": true, "msg": "操作成功", "data": {...}}
// 失败
{"code": 1, "ok": false, "msg": "操作失败", "data": null}
{ "code": 0, "ok": true, "msg": "操作成功", "data": {} }
```
## 异常处理优先级
| 字段 | 说明 |
|---|---|
| code | 0 成功;非 0 为业务/系统错误码 |
| ok | 布尔捷径(前端可只看它) |
| msg | 提示信息(前端默认 toast 文案) |
| data | 数据;无数据为 null |
```php
// app/ExceptionHandle.php
## 错误的三个来源
public function render($request, Throwable $e): Response
{
// 1. 参数验证失败
if ($e instanceof ValidateException) {
return json(['code' => 1, 'ok' => false, 'msg' => '参数验证失败', 'data' => $e->getError()]);
}
```text
apperr 错误(consts 哨兵 / apperr.Newf)
→ 按其携带的 code/msg 原样输出(如 401/403/404/422 业务码)
// 2. 数据不存在
if ($e instanceof ModelNotFoundException || $e instanceof DataNotFoundException) {
return json(['code' => 1, 'ok' => false, 'msg' => '数据不存在']);
}
普通 error(含 binding 校验失败、gorm 错误)
→ 映射为通用失败码,msg 取错误文本
// 3. JWT 异常(code=401)
if ($e instanceof \RuntimeException && in_array($e->getCode(), [401])) {
return json(['code' => 1, 'ok' => false, 'msg' => $e->getMessage()]);
}
// 4. HTTP 异常
if ($e instanceof HttpException) {
return json(['code' => 1, 'ok' => false, 'msg' => $e->getMessage() ?: '请求错误']);
}
// 5. 其他未知异常(生产环境隐藏详情)
$msg = config('app.show_error_msg') ? $e->getMessage() : '操作失败';
return json(['code' => 1, 'ok' => false, 'msg' => $msg]);
}
panic
→ Response 中间件内 recovery 捕获 → 500 信封 + 错误栈日志
→ 最外层 gin.CustomRecovery 兜底(防御静态资源等业务链之外的 panic)
```
:::danger 生产环境安全
`SHOW_ERROR_MSG` 必须设为 `false`,否则未知异常的详细信息(可能包含 SQL 语句、文件路径等敏感信息)会暴露给前端。
:::
## 拦截点前移的错误
## 统一响应类(Result)
部分错误在到达 handler 前就被中间件终结并直接写信封:401(JWT)、403(Authz)、限流拒绝、演示拒绝、租户拒绝。这类请求不产生操作日志,metrics 有分类计数。
```php
// extend/response/Result.php
## 无路由与静态兜底
Result::success($data, $msg); // 成功响应
Result::page($records, $total, ...); // 分页响应
Result::fail($msg, $code); // 失败响应
Result::unauthorized($msg); // 401 未授权
Result::forbidden($msg); // 403 禁止访问
Result::notFound($msg); // 404 数据不存在
Result::validateError($errors); // 422 验证错误
```text
NoRoute → JSON 404 信封("接口不存在: METHOD /path")
CustomRecovery → JSON 500 信封(业务链外兜底)
```
## 状态码说明
## 前端行为
| code | 含义 | 使用场景 |
|------|------|----------|
| 0 | 成功 | 所有成功操作 |
| 1 | 一般失败 | 业务逻辑失败 |
| 401 | 未授权 | Token 缺失/无效/过期 |
| 403 | 禁止访问 | 权限不足 / 租户异常 / 演示环境 |
| 404 | 数据不存在 | 查询单条记录为空 |
| 422 | 参数验证失败 | 表单验证不通过 |
- 拦截器解包:ok=true 直接返回 data
- ok=false / HTTP 错误 → toast msg,特定 code(401)触发刷新或跳登录
- 不在业务代码里重复判断 HTTP 状态(信封即事实)
## 业务错误码规范
- 定义在 `internal/consts/biz_err.go`,哨兵形式全局共享
- 新增错误码:编号段规划 + 中文 msg + 语义化命名(CodeRecordNotFound / CodeRecordExists)
- 详见 [8.2 错误码对照表](/zh/api-spec/error-code.html)
## 下一步
→ [5.17 链路追踪与指标](/zh/core/trace-metrics.html)