将 request body 绑定到不同的结构体中

一般通过调用 c.Request.Body 方法绑定数据,但不能多次调用这个方法。

  1. type formA struct {
  2. Foo string `json:"foo" xml:"foo" binding:"required"`
  3. }
  4. type formB struct {
  5. Bar string `json:"bar" xml:"bar" binding:"required"`
  6. }
  7. func SomeHandler(c *gin.Context) {
  8. objA := formA{}
  9. objB := formB{}
  10. // c.ShouldBind 使用了 c.Request.Body,不可重用。
  11. if errA := c.ShouldBind(&objA); errA == nil {
  12. c.String(http.StatusOK, `the body should be formA`)
  13. // 因为现在 c.Request.Body 是 EOF,所以这里会报错。
  14. } else if errB := c.ShouldBind(&objB); errB == nil {
  15. c.String(http.StatusOK, `the body should be formB`)
  16. } else {
  17. ...
  18. }
  19. }

要想多次绑定,可以使用 c.ShouldBindBodyWith.

  1. func SomeHandler(c *gin.Context) {
  2. objA := formA{}
  3. objB := formB{}
  4. // 读取 c.Request.Body 并将结果存入上下文。
  5. if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
  6. c.String(http.StatusOK, `the body should be formA`)
  7. // 这时, 复用存储在上下文中的 body。
  8. } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
  9. c.String(http.StatusOK, `the body should be formB JSON`)
  10. // 可以接受其他格式
  11. } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
  12. c.String(http.StatusOK, `the body should be formB XML`)
  13. } else {
  14. ...
  15. }
  16. }
  • c.ShouldBindBodyWith 会在绑定之前将 body 存储到上下文中。 这会对性能造成轻微影响,如果调用一次就能完成绑定的话,那就不要用这个方法。
  • 只有某些格式需要此功能,如 JSON, XML, MsgPack,ProtoBuf。 对于其他格式, 如 Query, Form, FormPost, FormMultipart可以多次调用 c.ShouldBind() 而不会造成任任何性能损失 (详见 #1341)。