JSON中文乱码需禁用HTML转义:调用c.Header设置UTF-8编码,并用json.NewEncoder(c.Writer).SetEscapeHTML(false).Encode(data);XML需显式添加xml标签;文件下载须正确设置Content-Disposition及Content-Type;自定义格式需实现render.Render接口。

JSON输出时中文乱码怎么办
Gin默认用json.Marshal序列化,会把中文转成Unicode转义(如"\u4f60\u597d"),前端看着像乱码。这不是bug,是Go标准库的默认行为,但实际API开发中几乎没人要这种输出。
解决方法是用gin.Context.JSON前先调用c.Header("Content-Type", "application/json; charset=utf-8"),再配合json.Encoder手动控制编码:
c.Header("Content-Type", "application/json; charset=utf-8")
encoder := json.NewEncoder(c.Writer)
encoder.SetEscapeHTML(false) // 关键:禁用HTML转义
encoder.Encode(data)
-
SetEscapeHTML(false)必须加,否则<code>>等字符也会被转义 - 别用
c.String()拼JSON字符串,易出错且不安全 - 如果用了
gin.H{}直接传给c.JSON(),它内部仍会转义中文——这是Gin 1.9+的默认行为,无法绕过,只能换手动编码
返回XML需要额外注册Struct标签
Gin的c.XML()底层调用xml.Marshal,但Go原生XML序列化对字段名敏感,且默认忽略未导出字段和无标签字段。
常见错误是结构体字段没加xml标签,结果返回空XML或只有一层<response></response>:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
type User struct {
ID int `json:"id" xml:"id"` // 必须显式声明xml标签
Name string `json:"name" xml:"name"`
Age int `json:"age" xml:"age"`
}
- XML标签名大小写必须和字段名一致,
xml:"Name"不会生效(首字母大写字段对应小写标签) - 想让某个字段不输出,用
xml:"-";想作为属性而非子元素,写成xml:"id,attr" - 根元素名由
c.XML()第一个参数决定,不是结构体名,例如c.XML(200, gin.H{"user": u})会包一层<user>...</user>
二进制文件下载时Content-Disposition容易写错
用c.Data()或c.File()发文件,前端没触发下载而是直接渲染/报错,大概率是Content-Disposition头格式不对。
Gin不自动加这个头,必须手动设:
c.Header("Content-Description", "File Transfer")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition", `attachment; filename="report.pdf"`)
c.Header("Content-Type", "application/pdf")
c.File("./reports/report.pdf")
- 文件名含中文?必须用
filename*=UTF-8''xxx.pdf格式,且xxx.pdf要URL编码,例如filename*=UTF-8''%E6%8A%A5%E5%91%8A.pdf -
c.File()会读整个文件进内存,大文件(>50MB)建议改用c.DataFromReader()流式传输 - 别漏掉
Content-Type,浏览器靠它判断是否可内嵌显示,设错会导致PDF打开失败
自定义格式响应(如YAML、MsgPack)得自己注册Render
Gin原生不支持YAML或MsgPack,强行用c.Data()会丢失状态码和Header统一管理能力。
正确做法是实现render.Render接口并注册到gin.Engine:
type YAMLRender struct {
Data interface{}
}
func (r YAMLRender) Render(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/yaml; charset=utf-8")
return yaml.NewEncoder(w).Encode(r.Data)
}
func (r YAMLRender) WriteContentType(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/yaml; charset=utf-8")
}
// 使用时:
c.Render(200, YAMLRender{Data: data})
- 必须同时实现
Render()和WriteContentType(),否则c.Render()不生效 - MsgPack同理,用
github.com/vmihailenco/msgpack/v5替换yaml包即可 - 别在handler里直接
fmt.Fprint(w, ...),会跳过Gin的error recovery和日志中间件
多格式共存时,别依赖Accept头自动分发——Gin没内置内容协商,得自己解析c.GetHeader("Accept")然后if-else分支处理,这点很容易被忽略。

















