首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Go 标准库提案:更优雅的 HTTP 内容 negotiation(协商)

Go 标准库提案:更优雅的 HTTP 内容 negotiation(协商)

作者头像
萝卜要努力
发布2026-01-20 15:17:51
发布2026-01-20 15:17:51
1180
举报
文章被收录于专栏:萝卜要加油萝卜要加油

背景

在 Web 开发中,HTTP 内容协商(Content Negotiation[1])是一项重要的机制,它允许客户端和服务器就响应内容的最佳格式达成一致。例如,手机端可能希望接收 JSON 格式的数据,Web端可能想要XML格式的数据,服务端可以在一个接口中提供多种数据格式。客户端通过 Accept 请求头表达自己的偏好,服务端根据这些偏好选择最合适的格式返回。 具体内容可以参考RFC9110[2]

比如在下面的例子中,使用Gin框架自己实现的内容协商[3] 。在一个API接口中返回了JSON和XML两种格式。

代码语言:javascript
复制

package main

import (
"net/http"

"github.com/gin-gonic/gin"
)

type User struct {
 Name string`json:"name"`
 Age  int    `json:"age"`
}

func main() {
 r := gin.Default()

 r.GET("/", func(c *gin.Context) {
  user := User{Name: "John Doe", Age: 30}
  c.Negotiate(http.StatusOK, gin.Negotiate{Offered: []string{"application/json", "application/xml"},
   Data: user,
  })
 })
 r.Run() // listen and serve on 0.0.0.0:8080
}

客户端根据自己的需求,支持返回 JSONXML格式的User。

代码语言:javascript
复制

➜  blog-example git:(main) ✗ curl -H "Accept: application/json" localhost:8080 
{"name":"John Doe","age":30}%                                                                                 
➜  blog-example git:(main) ✗ curl -H "Accept: application/xml" localhost:8080 
<User><Name>John Doe</Name><Age>30</Age></User>%

这种做法一个显而易见的好处就是不用为不同格式的数据提供多个接口。然而 Go 标准库 net/http 中缺少对内容协商的直接支持。开发者通常需要借助第三方库或自行实现相关逻辑,这既增加了开发成本,也可能存在一些潜在问题。比如 gin 的实现不支持质量因素(It does not handle quality factors)等。

为了解决这个问题,Go 社区在2017年就提出了一个提案 Proposal: x/net/http: support content negotiation[4],旨在为 net/http包添加内容协商的支持,从而提供更优雅的解决方案。在,这个提案终于有了实质性进展[5])(可能不会出现在Go 1.26)。

提案内容

该提案建议在 golang.org/x/net/http 包中新增一个 httpcontent 子包,其中包含一个名为 Negotiate 的函数。该函数的基本签名如下:

代码语言:javascript
复制

func Negotiate(accepts []string, offers []string) string
  • accepts 参数:表示客户端可接受的内容类型,通常来自 Accept 请求头。
  • offers 参数:表示服务器提供的可用内容类型。
  • 返回值:表示协商后确定的最佳内容类型。 Negotiate 函数的设计灵感来源于 golang/gddo/httputil 包中的 NegotiateContentType 函数,但它更加通用,可以处理各种类型的 Accept 头,例如 Accept-EncodingAccept-Language 等。 最终经过@rsc拍板,这个函数被放在 net/http 中。(net/http 不是冻结了吗?🤐) https://github.com/golang/go/issues/19307#issuecomment-2656875317[6]

优势与权衡

  • 优势:
    • 更简洁的 API:http.Negotiate 函数提供了一个简单而强大的接口,可以处理各种内容协商场景。
    • 更广泛的适用性:该函数不仅限于 Accept 头,还可以用于其他类型的 Accept-* 头。
    • 更高的效率:httpcontent.Negotiate 函数的实现经过优化,可以提供良好的性能。
  • 权衡:
    • 需要用户自行解析 Accept 头:httpcontent.Negotiate 函数要求用户将 Accept 头解析为字符串切片。
    • 错误处理:如果 Accept 头无法解析,httpcontent.Negotiate 函数将返回空字符串,开发者需要自行处理这种情况。

总结

Go 社区的这项提案旨在为 net/http 包添加内容协商的官方支持,这将大大简化 Web 开发者的工作。通过 http.Negotiate 函数,开发者可以更轻松地实现灵活而高效的内容协商,提升 Web 应用的用户体验。

目前,该提案的代码已经提交到 Go 官方仓库,并正在进行评审。如果一切顺利,我们有望在未来的 Go 版本中看到这个实用的新特性。

引用链接

[1]Content Negotiation: https://www.rfc-editor.org/rfc/rfc9110.html#name-content-negotiation

[2]RFC9110: https://www.rfc-editor.org/rfc/rfc9110.html#name-content-negotiation

[3]内容协商: https://pkg.go.dev/github.com/gin-gonic/gin#Context.NegotiateFormat

[4] Proposal: x/net/http: support content negotiation: https://github.com/golang/go/issues/19307

[5]实质性进展: https://go-review.googlesource.com/c/net/+/642956

[6]https://github.com/golang/go/issues/19307#issuecomment-2656875317

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萝卜要加油 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 提案内容
  • 优势与权衡
  • 总结
  • 引用链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档