gin多次绑定请求参数

package main

import (
	"fmt"
	"net/http"
	"time"

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

// resp 返回
func resp(c *gin.Context, code int, msg string) {
	c.JSON(http.StatusOK, gin.H{
		"code":    code,
		"msg":     msg,
		"nowtime": time.Now().Unix(),
	})
}

// AuthMiddleware 认证中间件
func authMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 中间件内bind参数
		var l loginReq
		if err := c.ShouldBind(&l); err != nil {
			fmt.Println("err: ", err)
			c.Abort()
		}

		if l.UserName == "" && l.Password == "" {
			c.Abort()
		}
	}
}

// login 登录逻辑
func login(c *gin.Context) {
	// 逻辑内再次bind参数
	// 此时这里的shouldBind会出错, 错误是: EOF
	var lr loginReq
	if err := c.ShouldBind(&lr); err != nil {
		fmt.Printf("bind params err: %v\n", err)
		resp(c, -1, err.Error())
		return
	}

	resp(c, 0, fmt.Sprintf("%s login success!", lr.UserName))
	return
}

// loginReq 请求参数
type loginReq struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

func main() {
	e := gin.Default()
	// 注册全局中间件
	e.Use(authMiddleware())

	e.POST("/api/v1/login", login)

	e.Run(":8080")
}

请求验证:

curl -XPOST 'http://127.0.0.1:8080/api/v1/login' \
-H 'Content-Type: application/json' \
-d '{
    "username":"zhangsan",
    "password":"123456"
}'

响应:

{
    "code": -1,
    "msg": "EOF",
    "nowtime": 1662451101
}

一、 使用ShouldBindBodyWith解决

// tips:
//      c.ShouldBindBodyWith在绑定之前将 body 存储到上下文中。这对性能有轻微的影响,因此如果您足够一次调用绑定,则不应使用此方法。
package main

import (
	"fmt"
	"net/http"
	"time"

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

// resp 返回
func resp(c *gin.Context, code int, msg string) {
	c.JSON(http.StatusOK, gin.H{
		"code":    code,
		"msg":     msg,
		"nowtime": time.Now().Unix(),
	})
}

// AuthMiddleware 认证中间件
func authMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 中间件内bind参数
		var l loginReq
		if err := c.ShouldBindBodyWith(&l, binding.JSON); err != nil {
			fmt.Println("err: ", err)
			c.Abort()
		}

		if l.UserName == "" && l.Password == "" {
			c.Abort()
		}
	}
}

// login 登录逻辑
func login(c *gin.Context) {
	// 逻辑内再次bind参数
	// 此时这里的shouldBind不会出错
	var lr loginReq
	if err := c.ShouldBindBodyWith(&lr, binding.JSON); err != nil {
		fmt.Printf("bind params err: %v\n", err)
		resp(c, -1, err.Error())
		return
	}

	resp(c, 0, fmt.Sprintf("%s login success!", lr.UserName))
	return
}

// loginReq 请求参数
type loginReq struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

func main() {
	e := gin.Default()
	// 注册全局中间件
	e.Use(authMiddleware())

	e.POST("/api/v1/login", login)

	e.Run(":8080")
}

二、 转存Body

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

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

// resp 返回
func resp(c *gin.Context, code int, msg string) {
	c.JSON(http.StatusOK, gin.H{
		"code":    code,
		"msg":     msg,
		"nowtime": time.Now().Unix(),
	})
}

// AuthMiddleware 认证中间件
func authMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 先把body取出来
		data, err := c.GetRawData()
		if err != nil {
			fmt.Println("read body failed, error: ", err)
		}

		// bind之前把body写回去
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))
		var l loginReq
		if err := c.ShouldBind(&l); err != nil {
			fmt.Println("err: ", err)
			c.Abort()
		}

		// bind之后把body写回去
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))
		if l.UserName == "" && l.Password == "" {
			c.Abort()
		}
	}
}

// login 登录逻辑
func login(c *gin.Context) {
	// 逻辑内再次bind参数
	// 此时这里的shouldBind不会出错
	var lr loginReq
	if err := c.ShouldBind(&lr); err != nil {
		fmt.Printf("bind params err: %v\n", err)
		resp(c, -1, err.Error())
		return
	}

	resp(c, 0, fmt.Sprintf("%s login success!", lr.UserName))
	return
}

// loginReq 请求参数
type loginReq struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

func main() {
	e := gin.Default()
	// 注册全局中间件
	e.Use(authMiddleware())

	e.POST("/api/v1/login", login)

	e.Run(":8080")
}

推荐使用第二种方式解决

09-06 16:32