Kratos接口中文乱码主因是响应头缺失Content-Type: application/json; charset=utf-8,导致客户端按GBK等默认编码错误解析;需在handler开头或httpx.ServerOption中显式设置该头,并确认数据源本身为UTF-8编码。

Kratos 框架接口返回中文乱码,大概率不是代码写错了,而是 HTTP 响应体编码没对齐——默认 UTF-8 被当成 GBK 解析,或响应头缺失 Content-Type 字段导致客户端瞎猜。
为什么 Kratos 默认返回中文会乱码?
Kratos 基于 Go 的 net/http,本身不强制设置响应字符集。如果你用 json.Marshal 返回结构体,生成的是纯 UTF-8 字节流,但若没显式声明 Content-Type: application/json; charset=utf-8,某些旧版浏览器、Postman 旧版本或 Java 客户端(如 HttpURLConnection)可能按系统默认编码(如 Windows 上的 GBK)解码,结果就是“æŸä¸ªæ–‡å—”这类典型乱码。
- Go 标准库不会自动加
charset=utf-8到Content-Type头里 - Kratos 的
transport/http中间件默认也不补这个头 - 前端 fetch 或 axios 若未指定
responseType: 'text'+ 手动解码,也可能触发错误解析
在 Kratos 的 HTTP Server 中补全 Content-Type 头
最直接有效的做法:在注册 HTTP handler 前,统一注入 charset=utf-8。不要依赖中间件“全局加”,因为 JSON、Protobuf、纯文本响应的处理逻辑不同,必须在真正写响应前设置。
- 对 JSON 接口,在
httpx.NewServer的Middleware里加一个响应头写入逻辑,但注意它必须在next.ServeHTTP之后执行(否则响应已写出) - 更稳妥的做法是:在每个
func(ctx context.Context, w http.ResponseWriter, r *http.Request)处理函数开头手动设头:func myHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") // 后续 json.NewEncoder(w).Encode(...) } - 若用 Kratos 的
api/v1自动生成的 HTTP handler,可在internal/server/http.go的newHTTPServer函数中,把httpx.ServerOption改为:httpx.WithMiddleware( func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") next.ServeHTTP(w, r) }) }, )
客户端读取时别跳过 charset 解析
乱码常被误判为服务端问题,但实际出在客户端——尤其 Java、Python requests 或旧版浏览器。它们读取响应体时,若没从 Content-Type 头提取 charset,就会 fallback 到平台默认编码。
- Java
HttpURLConnection:必须调用getContentType()解析 header,再用InputStreamReader显式指定UTF_8,不能只靠BufferedReader默认构造 - Python
requests:一般没问题,但若手动调r.content再用.decode(),必须写r.content.decode('utf-8'),别信r.text自动推断(它可能错读 header) - 前端 fetch:
response.text()在现代浏览器中基本可靠;但若用response.arrayBuffer()+TextDecoder,务必传{ encoding: 'utf-8' }
排查时容易忽略的关键点
很多人改完服务端头还乱码,是因为没确认真实响应内容是否真为 UTF-8 字节——比如数据库字段本身存的就是 GBK 编码的乱码字节,服务端只是原样转发。这时候加再多 charset=utf-8 都没用。
- 用
curl -v http://localhost:8000/api/foo看原始响应头和响应体十六进制:curl -v http://localhost:8000/api/foo 2>&1 | hexdump -C | head -20,确认中文部分是否为合法 UTF-8 字节序列(如“你好”应是e4 bd a0 e5 a5 bd) - 检查 Kratos 读取的数据源:MySQL 连接是否配置了
?charset=utf8mb4;Redis 存的 JSON 是否由 GBK 环境写入 - 若用 Kratos 的
datastore或自定义repo,确保sql.DB初始化时设置了SetConnMaxLifetime和正确的charset参数


















