有时候一个服务器应用程序需要在相同的 URI 上提供资源不同表示形式。当然这也可以手动实现,检查 Accept 请求头,设置推送的请求表单的文本。然而,当你的程序管理更多的资源和不同形式的时候,这将变得非常痛苦,你需要检查 Accept-CharsetAccept-Encoding,设置一些服务器端优先级,正确处理错误等等。

有一些 Go 的 web 框架已经艰难地实现了这个特性,但是它们不能正确地做以下的事情:

  • 不处理 accept-charset
  • 不处理 accept-encoding
  • 不会发送 RFC 提出的一些错误状态码( 406 not acceptable)。

但是我对我来说幸运的是,Iris 始终遵循最佳实践和Web标准。

基于:

  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding

实现于:

  • https://github.com/kataras/iris/pull/1316/commits/8ee0de51c593fe0483fbea38117c3c88e065f2ef

示例(Example)

  1. type testdata struct {
  2. Name string `json:"name" xml:"Name"`
  3. Age int `json:"age" xml:"Age"`
  4. }

通过 gzip 编码算法将资源渲染为 application/json 或者 text/html 或者 application/xml

  • 当客户端的 Accept 头部包含上述之一时,
  • 如果接收的为空就声明为 JSON(首先声明)
  • 当客户端的 Accpet-Encoding 头包含 gzip 或者为空时
  1. app.Get("/resource", func(ctx iris.Context) {
  2. data := testdata{
  3. Name: "test name",
  4. Age: 26,
  5. }
  6. ctx.Negotiation().JSON().XML().EncodingGzip()
  7. _, err := ctx.Negotiate(data)
  8. if err != nil {
  9. ctx.Writef("%v", err)
  10. }
  11. })

或者在一个中间件中定义他们,然后在最后的处理器中以 nil 参数调用Negotiate

  1. ctx.Negotiation().JSON(data).XML(data).Any("content for */*")
  2. ctx.Negotiate(nil)
  3. app.Get("/resource2", func(ctx iris.Context) {
  4. jsonAndXML := testdata{
  5. Name: "test name",
  6. Age: 26,
  7. }
  8. ctx.Negotiation().
  9. JSON(jsonAndXML).
  10. XML(jsonAndXML).
  11. HTML("<h1>Test Name</h1><h2>Age 26</h2>")
  12. ctx.Negotiate(nil)
  13. })

文献资料(Documentation)

Context.Negotiotion 方法创建一次,返回 “协商构造器”,以便构建适用于特定文本类型,字符和编码算法的服务器端的可用内容。

  1. Context.Negotiation() *context.NegotiationBuilder

Context.Negotiotion 方法用于服务相同 URI 的不同表现形式的资源。当无法匹配 MIME 类型它返回 context.ErrContentNotSupported

  1. Context.Negotiate(v interface{}) (int, error)
  • v 接口可以是一个 iris.N 结构体类型值。
  • v 接口可以是任何实现了 context.ContentSelector 接口的值。
  • v 接口可以是任何实现了 context.ContentNegotiator 接口的值。
  • v 接口可以是结构体 (JSON、JSONP、XML、YAML), 或者 字符串 (TEXT,HTML),或者字节切片(Markdown,Binary),或者匹配的 MIME 类型的字节切片。
  • 如果 v 接口是 nil,将会使用 Context.Negotitation() 构造器的内容来替代,除此之外,v 接口覆写了构造器的内容(服务器的 MIME 类型通过其注册的、支持的 MIME 列表进行检索)
  • 通过 Negotiation() 返回的构造器 的 MIMETextJSONXMLHTML 等等方法设置MIME类型优先级。
  • 通过 Negotiation() 返回的构造器 的 Charset 方法设置字符串类型优先级。
  • 通过 Negotiation() 返回的构造器 的 Encoding 方法设置编码算法优先级。
  • 通过 Negotiation() 返回的构造器 的 AcceptOverrideXML 等等方法修改 Accept