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

    将 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)。