Gin 如何用 PureJSON 输出字面量 HTML 特殊字符而不是 JSON 默认转义
【免费下载链接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.项目地址: https://gitcode.com/GitHub_Trending/gi/gin
当你用 Gin 的c.JSON返回包含<、>、&的字符串时,响应体里看到的全是\u003c、\u003e、\u0026这类 Unicode 转义,前端拿到后需要再还原一次。如果你的接口直接面向 JSON 消费者(而不是把 JSON 嵌进 HTML 里),这些转义只会干扰可读性。Gin 提供c.PureJSON来解决这个问题:它在序列化前关闭 HTML 转义,让特殊字符以字面量形式输出。本文基于本仓库的 docs/doc.md 中 PureJSON 章节、渲染器实现与测试断言,给出一条可运行、可核对的最小操作路径。当前仓库 go.mod 声明go 1.25.0,文档中提到的 Go 1.6 限制(见下文)对当前版本自然满足。
为什么c.JSON会把 HTML 特殊字符转义
先明确默认行为是什么。Gin 的 JSON 编码器接口对SetEscapeHTML的注释写得很清楚(codec/json/api.go):
默认行为是把
&、<、>转义为\u0026、\u003c、\u003e,以避免把 JSON 嵌入 HTML 时可能引发的安全问题;在转义影响可读性的非 HTML 场景下,调用SetEscapeHTML(false)关闭这一行为。
c.JSON走的是Marshal路径,保留默认转义。仓库测试 context_test.go 里的TestContextRenderJSON就是这条默认行为的断言:输入H{"foo": "bar", "html": "<b>"},期望响应体为{"foo":"bar","html":"\u003cb\u003e"},Content-Type为application/json; charset=utf-8。这就是你遇到"字面量变转义"现象的直接来源。
PureJSON 的实现路径
c.PureJSON在 context.go 中定义为:
// PureJSON serializes the given struct as JSON into the response body. // PureJSON, unlike JSON, does not replace special html characters with their unicode entities. func (c *Context) PureJSON(code int, obj any) { c.Render(code, render.PureJSON{Data: obj}) }而渲染器 render/json.go 的PureJSON.Render做三件事:
- 写入
Content-Type: application/json; charset=utf-8; - 通过当前 JSON codec 创建
Encoder,调用encoder.SetEscapeHTML(false); - 调用
encoder.Encode(r.Data)。
注意Encode与Marshal不同:它会"在 JSON 编码后追加一个换行符"(codec/json/api.go 的注释)。所以PureJSON的响应体比c.JSON多一个结尾换行,仓库测试 render_test.go 的期望值{"foo":"bar","html":"<b>"}后正是带\n的。
写一个最小示例并核对输出
下面这个程序直接取自 docs/doc.md 的 PureJSON 章节,同时挂上/json和/purejson两条路由,方便对比转义前后的差异。保存为独立目录中的main.go(go mod init+go get github.com/gin-gonic/gin),然后运行:
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() // Serves unicode entities r.GET("/json", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "html": "<b>Hello, world!</b>", }) }) // Serves literal characters r.GET("/purejson", func(c *gin.Context) { c.PureJSON(http.StatusOK, gin.H{ "html": "<b>Hello, world!</b>", }) }) // listen and serve on 0.0.0.0:8080 r.Run(":8080") }go run .启动后分别请求两条路由:
curl http://localhost:8080/json curl http://localhost:8080/purejson判断标准以仓库测试断言为准:c.JSON对"html": "<b>"的期望输出是转义形式"\u003cb\u003e"(context_test.go);c.PureJSON的期望输出是字面量"<b>",且Content-Type为application/json; charset=utf-8(context_test.go 的TestContextRenderPureJSON)。如果/json返回\u003c...\u003e而/purejson返回<...>,说明行为与文档一致。
需要中断响应链时:AbortWithStatusPureJSON
如果你的场景是中间件里校验失败后要"中断链 + 返回未转义 JSON",可以用AbortWithStatusPureJSON。context.go 中的注释说明它先调用Abort()再内部调用PureJSON,停止处理链、写入状态码并返回不带转义的 JSON body,同时把Content-Type设为application/json。对应测试 TestContextAbortWithStatusPureJSON 断言了响应体未被转义({"foo":"fooValue","bar":"barValue"}形式的明文输出)且上下文处于 aborted 状态。
可选分支:替换 JSON codec 时 PureJSON 是否仍生效
Gin 默认用encoding/json,也可以按 docs/doc.md 的 Build Tags 章节在构建时替换 codec:
go build -tags=jsoniter . # 或 go build -tags=go_json . # 或 go build -tags=sonic .PureJSON.Render不是直接调用encoding/json,而是走 codec 抽象的json.API.NewEncoder(w)再SetEscapeHTML(false)(见上文 render/json.go)。各 codec 实现(如 codec/json/jsoniter.go 使用jsoniter.ConfigCompatibleWithStandardLibrary)都返回实现了同一Encoder接口的对象,该接口的SetEscapeHTML语义在 codec/json/api.go 中统一注明。因此按上述 build tag 替换 codec 构建时,PureJSON的关闭转义逻辑仍然走同一接口路径。
限制与核对清单
- 文档明确说明:"This feature is unavailable in Go 1.6 and lower",即 PureJSON 需要 Go 1.6 以上;当前仓库要求 Go 1.25.0(go.mod),正常拉取构建即可满足。
PureJSON输出带一个结尾换行符(Encode行为),这是与c.JSON输出的已知差异,来自 codec/json/api.go 的注释与 render_test.go 的断言。- 两种渲染器
Content-Type都是application/json; charset=utf-8,区别只在是否转义,不要依赖Content-Type来区分二者。 - 不依赖跑服务也可以核对:在本仓库根目录执行
go test -run TestRenderPureJSON ./render/和go test -run TestContextRenderPureJSON .,两条测试分别对应渲染器与Context层的字面量输出断言。
如果你发现/purejson的输出仍被转义,先确认代码路径确实改成了c.PureJSON(而不是c.JSON);文档中AbortWithStatusPureJSON注释里 "return a JSON body without escaping" 的描述表明,未转义行为是纯PureJSON渲染路径的预期,而非配置项可切换的选项。
【免费下载链接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.项目地址: https://gitcode.com/GitHub_Trending/gi/gin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考