首页 > 其他分享 >Gin 请求参数的获取和原理分析

Gin 请求参数的获取和原理分析

时间:2024-09-02 11:36:00浏览次数:5  
标签:return 请求 person 参数 func gin Gin name

这篇文章介绍 Gin 常用的获取请求参数的方式和它们内部的原理,让我们不纠结于参数的繁琐处理。

参数风格

对于 RESTful 风格的 http 请求来说,参数的表现会有下面几种方式:

URI 参数

什么是 URI 参数?RESTful 风格的请求,某些请求的参数会通过 URI 来表现。

举个简单的例子:张三通过网上银行给李四转了 500 元,这个路由可以这么设计:

xxx.com/:name/transfer/:money/to/:name

非常具体的体现:
xxx.com/zhangsan/transfer/500/to/lisi

当然你会说这个路由设计会比较丑陋,不过在 URI 里面增加参数有的时候是比较方便的,gin 支持这种方式获取参数。

// This handler will match /user/john but will not match /user/ or /user
router.GET("/user/:name", uriFunc)

对于获取这种路由参数,gin 提供了两种方式去解析这种参数。

方式1:Param

func uriFunc(c *gin.Context) {
    name := c.Param("name")
    c.String(http.StatusOK, "Hello %s", name)
}

方式2:bindUri

type Person struct {
   Name string `uri:"name" binding:"required"`
}

func uriFunc(c *gin.Context) {
  var person Person
  if err := c.ShouldBindUri(&person); err != nil {
     c.JSON(400, gin.H{"msg": err.Error()})
     return
  }
  c.JSON(200, gin.H{"name": person.Name)
}

其实现原理很简单,就是在创建路由树的时候,将路由参数以及对应的值放入一个特定的 map 中即可。

func (ps Params) Get(name string) (string, bool) {
    for _, entry := range ps {
      if entry.Key == name {
        return entry.Value, true
      }
    }
    return "", false
}

QueryString Parameter

query String 即路由的 ? 之后的所带的参数,这种方式是比较常见的。

例如:/welcome?firstname=Jane&lastname=Doe

这里要注意的是,不管是 GET 还是 POST 都可以带 queryString Parameter。我曾经遇到某公司所有的参数都挂在 query string 上,这样做其实是不建议的,不过大家都这么做,只能顺其自然了。这么做的缺点很明显:

  • 容易突破 URI 的长度限制,导致接口参数被截断。一般情况下服务器为了安全会对 URL 做长度限制,最大为2048
  • 同时服务器也会对传输的大小也是有限制的,一般是 2k
  • 当然这么做也是不安全的,都是明文的

这里就不具体罗列了,反正缺点挺多的。

这种参数也有两种获取方式:

方式1:Query

firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")

方式2:Bind

type Person struct {
   FirstName  string `form:"name"`
}

func queryFunc(c *gin.Context) {
    var person Person
    if c.ShouldBindQuery(&person) == nil {
        log.Println(person.Name)
    }
}

实现原理:其实很简单就是将请求参数解析出来而已,利用的 net/url 的相关函数。

//net/url.go:L1109
func (u *URL) Query() Values {
    v, _ := ParseQuery(u.RawQuery)
    return v
}

Form

Form 一般还是更多用在跟前端的混合开发的情况下。Form 可以用于所有的方法 POST,GET,HEAD,PATCH ……

这种参数也有两种获取方式:

方式1:

name := c.PostForm("name")

方式2:

type Person struct {
    Name string `form:"name"`
}

func formFunc(c *gin.Context) {
    var person Person
    if c.ShouldBind(&person) == nil {
        log.Println(person.Name)
    }
}

Json Body

Json Body 是被使用最多的方式,基本上各种语言库对 json 格式的解析非常完善了,而且还在不断的推陈出新。

gin 对 json 的解析只有一种方式。

type Person struct {
    Name string `json:"name"`
}

func jsonFunc(c *gin.Context) {
    var person Person
    if c.ShouldBind(&person) == nil {
        log.Println(person.Name)
    }
}

gin 默认是使用的 go 内置的 encoding/json 库,内置的 json 在 go 1.12 后性能得到了很大的提高。不过 Go 对接 PHP 的接口,如果用内置的 json 库简直就是一种折磨,gin 可以使用 jsoniter 来代替,只需要在编译的时候加上标志即可:"go build -tags=jsoniter .",强烈建议对接 PHP 接口的同学,尝试 jsoniter 这个库,让你不再受 PHP 接口参数类型不确定之苦。

当然 gin 还支持其他类型参数的解析,如 Header,XML,YAML,Msgpack,Protobuf 等,这里就不再具体介绍了。

Bind 系列函数的源码剖析

使用 gin 解析 request 的参数,按照我的实践来看,使用 Bind 系列函数还是比较好一点,因为这样请求的参数会比较好归档、分类,也有助于后续的接口升级,而不是将接口的请求参数分散不同的 handler 里面。

初始化 binding 相关对象

gin 在程序启动就会默认初始化好 binding 相关的变量

// binding:L74
var (
 JSON          = jsonBinding{}
 XML           = xmlBinding{}
 Form          = formBinding{}
 Query         = queryBinding{}
 FormPost      = formPostBinding{}
 FormMultipart = formMultipartBinding{}
 ProtoBuf      = protobufBinding{}
 MsgPack       = msgpackBinding{}
 YAML          = yamlBinding{}
 Uri           = uriBinding{}
 Header        = headerBinding{}
)

ShoudBind 与 MustBind 的区别

bind 相关的系列函数大体上分为两类 ShoudBind 和 MustBind。实现上基本一样,为了有区别的 MustBind 在解析失败的时候,返回 HTTP 400 状态。

MustBindWith:

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
}

ShoudBindWith:

func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
   return b.Bind(c.Request, obj)
}

匹配对应的参数 decoder

不管是 MustBind 还是 ShouldBind,总体上解析又可以分为两类:一种是让 gin 自己判断使用哪种 decoder,另外一种就是指定某种 decoder。自己判断使用哪种 decoder 比 指定 decoder 多了一步判断,其他的都是一样的。

func (c *Context) ShouldBind(obj interface{}) error {
    b := binding.Default(c.Request.Method, c.ContentType())
    return c.ShouldBindWith(obj, b)
}

func Default(method, contentType string) Binding {
    if method == http.MethodGet {
        return Form
    }

    switch contentType {
    case MIMEJSON:
        return JSON
    case MIMEXML, MIMEXML2:
        return XML
    case MIMEPROTOBUF:
        return ProtoBuf
    case MIMEMSGPACK, MIMEMSGPACK2:
        return MsgPack
    case MIMEYAML:
        return YAML
    case MIMEMultipartPOSTForm:
        return FormMultipart
    default: // case MIMEPOSTForm:
        return Form
    }
}

ShouldBind/MustBind 会根据传入的 ContentType 来判断该使用哪种 decoder。不过对于 Header 和 Uri 方式的参数,只能用指定方式的decoder 了。

总结

本篇文章主要介绍了 gin 是如何快速处理客户端传递过的参数的。写文章不易请大家帮忙点击 在看,点赞,分享。

 

标签:return,请求,person,参数,func,gin,Gin,name
From: https://www.cnblogs.com/cheyunhua/p/18392418

相关文章

  • 有没有办法通过 PHP 或 .htaccess 来区分资源请求者
    是的,你可以通过PHP或.htaccess来区分资源请求者。在PHP中,你可以使用$_SERVER['REMOTE_ADDR']变量来获取请求者的IP地址。你还可以使用其他变量来获取更多关于请求者的信息,例如$_SERVER['HTTP_USER_AGENT']来获取请求者的浏览器信息。在.htaccess中,你可以使用RewriteCo......
  • nginx-部署windows服务(亲测有效)
    WindowsServiceWrapper(简称winsw)下载winswhttps://repo.jenkins-ci.org/artifactory/releases/com/sun/winsw/winsw/进入链接页面选择对应的版本下载后缀名为net4.exe的文件重命名文件将winsw-2.9.0-net4.exe重命名为nginx-service.exe,并放置到nginx根目录中......
  • nginx 版本升级 转载
    Nginx的版本最开始使用的是Nginx-1.18.0,由于服务升级,需要将Nginx的版本升级到Nginx-1.19.7,要求Nginx不能中断提供服务。为了应对上述的需求,提供两种解决方案:方案1:makeupgrade完成升级方案2:服务信号完成升级nginx版本升级环境说明当前是CentOS7服务器。点击进......
  • Nginx多域名,多证书,多服务配置,实用版
    本文主要讲解在Nginx配置多个域名,多证书,多服务,核心是nginx配置文件。1.情景介绍现在有一个项目,有测试环境和正式环境,已经为两个环境各自申请到域名和ssl证书正式环境域名:xxx.com和www.xxx.com证书:xxx.com_bundle.pem和xxx.com.key测试环境域名:test-xxx.com和......
  • 15、java 面向对象之二:对象的创建和使用(对象内存解析和匿名对象)、再谈方法(方法的重
    java面向对象之二:Ⅰ、对象的创建和使用:1、对象的内存解析:其一、描述:其二、内存解析代码1为:其三、内存解析截图1为:其四、内存解析代码2为:其五、内存解析截图2为:2、匿名对象的使用:其一、描述:其二、代码为:其三、截图为:3、自定义数组的工具类:其一、描述:其二、代码为:A、Arr......
  • PHP转Go系列 | ThinkPHP与Gin框架之Redis延时消息队列技术实践
    大家好,我是码农先森。我们在某宝或某多多上抢购商品时,如果只是下了订单但没有进行实际的支付,那在订单页面会有一个支付倒计时,要是过了这个时间点那么订单便会自动取消。在这样的业务场景中,一般情况下就会使用到延时队列。通常在客户下单之后,就会将订单数据推送到延时队列中并且......
  • 以MySQL为例,来看看maven-shade-plugin如何解决多版本驱动共存的问题?
    开心一刻清明节那天,看到一小孩在路边烧纸时不时地偷偷往火堆里扔几张考试卷子边烧边念叨:爷爷呀,你岁数大了,在那边多做做题吧,对脑子好,要是有不懂的地方,就把我老师带走,让他教您!前提说明假设MySQL5.7.36的库qsl_datax有表qsl_datax_source和数据CREATETABLE`qsl_datax......
  • Vue, Avoided redundant navigation to current location: "/login".
    VueAvoidedredundantnavigationtocurrentlocation:"/login".=================================报错解释:这个错误是在使用Vue.js框架时,发生的一个警告,表示尝试进行一个冗余的导航到当前位置(即“/login”路径)。这通常发生在VueRouter中,当你尝试通过编程方式导航到当前正......
  • 保存模型 & 记录参数
    保存的模型在你提供的代码中,模型保存的条件如下:验证阶段(_valid_epoch方法):在每个epoch结束后,模型会进行验证,即使用验证数据集(self.valid_loader)计算验证指标(valid_metric)。通过self.valid_step方法计算每个batch的验证指标,最终将这些指标的平均值保存在valid_metric......
  • 【#第三期实战营闯关作业 ## MindSearch在 Hugging FaceSpace的部署】
    把MindSearch部署到GithubCodespace后,下一步就是上传到HuggingFaceSpace,以下是记录了实操的过程及截图:打开https://huggingface.co/spaces,并点击CreatenewSpace,如下图所示:在输入Spacename并选择License后,选择配置如下面截图所示:3进入Settings,配置硅基......