浅谈Gin框架中bind

概述

  • Gin框架中,有bind函数可以非常方便的将url的查询参数query parameter、http的Header,body中提交上来的数据格式,如form,json,xml等,绑定到go中的结构体中去,这期间Binding做了啥事情,这么多个Bindding函数,我们该如何选择,一起通过源码来解开其中神秘的面纱吧。

Binding接口

type Binding interface {
   Name() string
   Bind(*http.Request, interface{}) error
}
  • Binding是一个接口,在源码中,有10个实现了Binding的结构体,以及3个接口
    在这里插入图片描述

context.Bind

// Bind checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
//     "application/json" --> JSON binding
//     "application/xml"  --> XML binding
// otherwise --> returns an error.
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
// It decodes the json payload into the struct specified as a pointer.
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
func (c *Context) Bind(obj interface{}) error {
	b := binding.Default(c.Request.Method, c.ContentType())
	return c.MustBindWith(obj, b)
}

cnotext.MustBindWith

// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
   if err := c.ShouldBindWith(obj, b); err != nil {
      c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
      return err
   }
   return nil
}
  • 从注解和源码可以看出,MustBindWith最终也是调用了SouldBindWith,并且对ShouldBindWith的结果进行了判断,如果有错误,则以http 400的状态码进行退出。

ShouldBindWith

// ShouldBindWith binds the passed struct pointer using the specified binding engine.
// See the binding package.
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
   return b.Bind(c.Request, obj)
}
  • 这个方法是所有其他绑定方法的一个基础,基本上所有的绑定方法都需要用到这个方法来对数据结构进行一个绑定

  • 以上为主要的bingding的过程,其他派生出来的如BindJSON、ShouldBindJSON等,为具体的数据类型的快捷方式而已,只是帮我们把具体的bingding的数据类型提前给封装了起来而已,如Json格式的bingding函数

context.BindJSON

// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func (c *Context) BindJSON(obj interface{}) error {
   return c.MustBindWith(obj, binding.JSON)
}
  • context.BindJSON从源码上分析,可以看到,仅仅比Bind方法少了一句
b := binding.Default(c.Request.Method, c.ContentType())
  • 这一句是为了判断当前的请求方法和contentType,来给context.MustBindWith传的一个具体的bingding类型。

  • Json的实现的Binding接口如下

func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
   if req == nil || req.Body == nil {
      return fmt.Errorf("invalid request")
   }
   return decodeJSON(req.Body, obj)
}
  • jsonBinding结构体实现了Binding接口的Bind方法,将请求过来的Body数据进行解码,绑定到obj里面去

context.ShouldBindJSON

// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (c *Context) ShouldBindJSON(obj interface{}) error {
   return c.ShouldBindWith(obj, binding.JSON)
}
  • 从源码的注解来看,ShouldBindJSON其实就是ShouldBindWith(obj, binding.JSON)的快捷方式,简单来说,就是在ShouldBindWith(obj, binding.JSON)上面固定了参数,当我们明确规定,body提交的参数内容为json时,简化了我们的调用和增强了代码的可读性。

context.ShouldBindUri()

// ShouldBindUri binds the passed struct pointer using the specified binding engine.
func (c *Context) ShouldBindUri(obj interface{}) error {
   m := make(map[string][]string)
   for _, v := range c.Params {
      m[v.Key] = []string{v.Value}
   }
   return binding.Uri.BindUri(m, obj)
}
  • 从url绑定采用的方法跟header和body的方式不一样,不需要传入一个实现Binding接口的结构体类型

context.ShouldBindUri()

// BindUri binds the passed struct pointer using binding.Uri.
// It will abort the request with HTTP 400 if any error occurs.
func (c *Context) BindUri(obj interface{}) error {
   if err := c.ShouldBindUri(obj); err != nil {
      c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
      return err
   }
   return nil
}
  • BindUri也是对ShouldBindUri的一个封装,多了一个对ShouldBindUri结果的一个判断

代码实例

代码如下

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)
type queryHeader struct {
	Myheader string `header:"myheader"`
	Mydemo string `header:"mydemo"`
}

type queryBody struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Sex int `json:"sex"`
}

type queryParameter struct {
	Year int `form:"year"`
	Month int `form:"month"`
}

type queryUri struct {
	Id int `uri:"id"`
	Name string `uri:"name"`
}

func bindUri(context *gin.Context){
	var q queryUri
	err:= context.ShouldBindUri(&q)
	if err != nil {
		context.JSON(http.StatusBadRequest,gin.H{
			"result":err.Error(),
		})
		return
	}
	context.JSON(http.StatusOK,gin.H{
		"result":"绑定成功",
		"uri": q,
	})
}

func bindQuery(context *gin.Context){
	var q queryParameter
	err:= context.ShouldBindQuery(&q)
	if err != nil {
		context.JSON(http.StatusBadRequest,gin.H{
			"result":err.Error(),
		})
		return
	}
	context.JSON(http.StatusOK,gin.H{
		"result":"绑定成功",
		"query": q,
	})
}

func bindBody(context *gin.Context){
	var q queryBody
	err:= context.ShouldBindJSON(&q)
	if err != nil {
		context.JSON(http.StatusBadRequest,gin.H{
			"result":err.Error(),
		})
		return
	}
	context.JSON(http.StatusOK,gin.H{
		"result":"绑定成功",
		"body": q,
	})
}

func bindhead(context *gin.Context){
	var q queryHeader
	err := context.ShouldBindHeader(&q)
	if err != nil {
		context.JSON(http.StatusBadRequest,gin.H{
			"result":err.Error(),
		})
		return
	}
	context.JSON(http.StatusOK,gin.H{
		"result":"绑定成功",
		"header": q,
	})
}

func main(){
	srv := gin.Default()
	srv.GET("/binding/header",bindhead)
	srv.GET("/binding/body",bindBody)
	srv.GET("/binding/query",bindQuery)
	srv.GET("/binding/:id/:name",bindUri)
	srv.Run(":9999")
}

运行结果

绑定Header数据

在这里插入图片描述

绑定QueryParameter数据

在这里插入图片描述

绑定Body Json数据

在这里插入图片描述

绑定Uri数据

在这里插入图片描述

总结

  • 使用gin框架中的bind方法,可以很容易对http请求过来的数据传递到我们的结构体指针去,方便我们代码编程。
  • 当参数比较简单,不需要结构体来进行封装时候,此时还需采用context的其他方法来获取对应的值
  • gin在bind的时候,未对结构体的数据进行有效性检查,如果对数据有强要求时,需要自己对结构体的数据内容进行判断
  • 建议在实践过程中,使用shouldBind<xxx>函数
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Gin框架,并不存在`onsubmit`这个概念,因为Gin框架是一个后端框架,主要用于处理HTTP请求和响应。如果您需要在前端页面使用`onsubmit`事件,可以使用HTML的`form`元素来实现。 以下是一个使用HTML表单和`onsubmit`事件的例子: ```html <form action="/api/user" method="POST" onsubmit="submitForm(event);"> <label for="username">Username:</label> <input type="text" id="username" name="username"> <label for="password">Password:</label> <input type="password" id="password" name="password"> <input type="submit" value="Submit"> </form> <script> function submitForm(event) { event.preventDefault(); // 阻止表单的默认提交行为 // 获取表单数据 var username = document.getElementById('username').value; var password = document.getElementById('password').value; // 使用AJAX发送POST请求 $.ajax({ url: '/api/user', method: 'POST', data: { username: username, password: password }, dataType: 'json', success: function(data) { // 处理成功响应 console.log('User created:', data); }, error: function(jqXHR, textStatus, errorThrown) { // 处理错误情况 console.error('Error:', textStatus, errorThrown); } }); } </script> ``` 在这个例子,我们定义了一个HTML表单,并使用`onsubmit`事件来触发`submitForm`函数。在函数,我们使用`event.preventDefault()`方法来阻止表单的默认提交行为,然后获取表单数据并使用AJAX发送POST请求。当成功接收到响应后,我们可以在`success`回调函数处理响应数据。在上面的例子,我们只是简单地将用户创建信息输出到控制台,但您可以根据需要进行其他处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值