Kratos 不处理 JSON 解析报错,因解析发生在 transport 层之下,需手动接管 io.ReadAll + json.Unmarshal 并捕获 *json.SyntaxError 等错误,升格为业务错误统一返回。

Kratos 本身不处理 JSON 解析报错,它依赖底层 HTTP 框架(如 kgin)或 Go 原生 json.Unmarshal 的行为;真正要解决的,是「谁在解析」和「错误发生时是否被框架捕获并转为标准响应」这两个问题。
HTTP 请求体解析失败时,kgin 默认 panic 且不走错误处理链
当你用 kgin.Bind() 或类似方式自动解析请求体(如 *v1.UserRequest)时,若 body 是非法 JSON,kgin 内部调用 json.Unmarshal 失败会直接 panic —— 这个 panic 会被 recovery.Recovery 中间件捕获,但返回的是框架默认的 500 错误,reason 字段为 INTERNAL_ERROR,不包含原始 JSON 错误详情(比如 Expecting value: line 1 column 1 (char 0))。
- 这不是 Kratos 的 bug,而是 Gin/Kitex 等底层框架的设计选择:解析失败属于「协议层异常」,不是业务逻辑错误
- 你无法靠
errors.Newf在 Handler 里提前拦截,因为解析发生在 Bind 阶段,早于你的业务代码执行 - 如果想暴露具体解析位置(如列号),必须自己接管解析逻辑,绕过
kgin.Bind()
手动解析 + 容错包装是唯一可控方案
放弃自动 Bind,改用 io.ReadAll(r.Body) 拿到原始字节,再用带 try-catch 思路的封装函数解析。这样你能控制错误类型、补充上下文、甚至记录原始 payload。
- 先检查
r.Header.Get("Content-Type")是否含application/json,避免非 JSON 请求误入 - 用
json.RawMessage做预校验:尝试json.Unmarshal(body, &json.RawMessage{}),成功说明结构合法,再二次解析到目标 struct - 捕获
*json.SyntaxError和*json.UnmarshalTypeError,提取Error().pos或Offset字段,构造带定位信息的业务错误:errors.Newf(40001, "invalid json at offset %d", syntaxErr.Offset) - 注意:原始
r.Body只能读一次,手动解析后需用http.MaxBytesReader做大小限制,防止 OOM
前端传参不规范时,Nginx 层清洗比代码层容错更高效
很多 JSON 解析失败其实源于上游——BOM 头、多余空格、HTML 注释混入、换行符未转义等。这类问题在 Go 层做兼容成本高、易漏,更适合前置拦截。
- 在 Nginx 配置中加
map $content_type $invalid_json { "~*application/json" 1; },配合if ($invalid_json) { ... }做基础过滤 - 用 Lua 模块(如
lua-resty-json)在入口做轻量校验,非法时直接 400 返回,不进后端 - Kratos 的
transport/http.Server不支持中间件修改原始Body,所以「自动 strip BOM」这种逻辑必须放在反向代理层,不能指望框架
最常被忽略的一点:Kratos 的错误体系只覆盖「业务逻辑抛出的 error」,而 JSON 解析失败属于「传输协议崩溃」,它发生在 transport 层之下,框架根本没机会用 (errors.code) 注解去映射状态码。想让这类错误也进入统一监控和文档生成,唯一的办法是把它「升格」为业务错误——也就是手动解析 + 显式调用 errors.Newf,而不是依赖任何自动机制。


















