百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Gin框架参数多次绑定(gin框架配置文件)

ccwgpt 2024-09-18 12:20 47 浏览 0 评论

在我们写的绝大部分的API代码当中,其实都是需要传递参数的,无论是通过 path、query string 还是 body,在 gin 当中,为我们提供了一系列的 binding 方法让我们可以把这些参数绑定到一个对象中,通过还可以通过 struct tag 来对参数进行校验,不知道到大家曾今是否和遇到过相同的困惑:

  • 我创建/更新接口有时候就仅仅只相差一个 id,我是不是可以复用代码?
  • 是否可以直接用 model 层的 struct 绑定参数?

转自:https://zhuanlan.zhihu.com/p/91312616

整理:地鼠文档:www.topgoer.cn

接下来本文就从这些问题出发,利用 go 的组合特点,介绍一些参数绑定上的小技巧

参数绑定技巧

1. 复用创建/更新时的参数

// UserParams 用户参数
type UserParams struct {
    Name     string `json:"name" binding:"required"`
    Password string `json:"password" binding:"required"`
}

// CreateUserParams 创建用户
type CreateUserParams struct {
    UserParams
}

// UpdateUserParams 更新用户
type UpdateUserParams struct {
    UserParams
    ID uint `json:"id" binding:"required"`
}

2. 用 model 层的 struct 绑定参数

如果我们在参数绑定的时候,向上面那样,每个参数单独创建一个绑定,这样在 Model 层创建数据库记录的时候就需要去手动的转换一道了,如果每个都需要这么写,会感觉很麻烦。

这是原本的 user 表

// model/model.go

// Model default model
type Model struct {
    ID        uint       `json:"id" gorm:"primary_key"`
    CreatedAt time.Time  `json:"created_at"`
    UpdatedAt time.Time  `json:"updated_at"`
    DeletedAt *time.Time `json:"deleted_at" sql:"index"`
}

// model/user.go

// User 用户表
type User struct {
    Model

    Name     string `json:"name"`
    Password string `json:"password"`
}

这时我们可以稍微改造一下, 利用 binding:”-“ 忽略字段的功能,和 struct 组合,将 api 的参数和 model 关联在一起,减少一些结构体转换的代码

// model/model.go

// Model default model
type Model struct {
    ID        uint       `json:"id" gorm:"primary_key"`
    CreatedAt time.Time  `json:"created_at" binding:"-"`
    UpdatedAt time.Time  `json:"updated_at" binding:"-"`
    DeletedAt *time.Time `json:"deleted_at" sql:"index" binding:"-"`
}

// model/user.go

// User 用户表
type User struct {
    Model

    Name     string `json:"name" binding:"required"`
    Password string `json:"password" binding:"required"`
}

// api/user.go
// UserParams 用户参数
type UserParams struct {
    model.User
}

// CreateUserParams 创建用户
type CreateUserParams struct {
    UserParams
    ID    uint    `json:"id" gorm:"primary_key" binding:"-"`
}

// UpdateUserParams 更新用户
type UpdateUserParams struct {
    UserParams
}

3. 使用 ShouldBind 而不是 Bind

Bind 方法会自动将 http status 设置为 400, 然后报错,但是我们往往会需要携带更多的信息返回,或者返回不同的 status 这时候往往会出现下面这样的警告,而使用 ShouldBind 可以避免此类问题

[WARNING] Headers were already written. Wanted to override status code 400 with 200

4. 多次绑定 request body 数据

这是官方文档的一个示例,一般情况第二次读取 request body 的数据就会出现 EOF 的错误,因为 c.Request.Body 不可以重用

type formA struct {
  Foo string `json:"foo" binding:"required"`
}

type formB struct {
  Bar string `json:"bar" binding:"required"`
}

func SomeHandler(c *gin.Context) {
  objA := formA{}
  objB := formB{}
  // c.ShouldBind 使用了 c.Request.Body,不可重用。
  if errA := c.ShouldBind(&objA); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // 因为现在 c.Request.Body 是 EOF,所以这里会报错。
  } else if errB := c.ShouldBind(&objB); errB == nil {
    c.String(http.StatusOK, `the body should be formB`)
  } else {
    ...
  }
}

gin 1.4 之后官方提供了一个 ShouldBindBodyWith 的方法,可以支持重复绑定,原理就是将 body 的数据缓存了下来,但是二次取数据的时候还是得用 ShouldBindBodyWith 才行,直接用 ShouldBind 还是会报错的。

func SomeHandler(c *gin.Context) {
  objA := formA{}
  objB := formB{}
  // 读取 c.Request.Body 并将结果存入上下文。
  if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // 这时, 复用存储在上下文中的 body。
  } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
    c.String(http.StatusOK, `the body should be formB JSON`)
  // 可以接受其他格式
  } else {
    ...
  }
}

这种方式其实有个问题,什么情况下我们会去多次读取 body 的数据,其实在中间件中我们是需要用到,有的中间件需要读取参数做一些处理,例如权限中间件需要获取当前资源的id,判断当前用户是否有权限,如果这个时候直接使用 ShouldBindBodyWith 会导致之后的所有的接口都修改才行,十分的不优雅,下面提供一种不用影响后续使用的方法

func startPage(c *gin.Context) {
    var (
        p1 Person
        p2 Person
    )
    buf := &bytes.Buffer{}
    tea := io.TeeReader(c.Request.Body, buf)
    body, err := ioutil.ReadAll(tea)
    if err != nil {
        log.Panicf("read body err: %+v", err)
    }
    c.Request.Body = ioutil.NopCloser(buf)
    // read buf ...
    if err := binding.JSON.BindBody(body, &p1); err != nil {
        log.Panic("p1", err)
    }
    log.Println("p1", p1)
    // use ShouldBind again ..
    if err := c.ShouldBind(&p2); err != nil {
        log.Panic("p2", err)
    }
    log.Println("p2", p2)
    c.String(200, "Success")
}


// output
// 2019/11/06 23:10:04 p1 {hello world}
// 2019/11/06 23:10:04 p2 {hello world}
// [GIN] 2019/11/06 - 23:10:04 | 200 |   27.0400917s |             ::1 | POST     /testing

这三行也适用于其他需要多次读取 io.Reader 的情况

buf := &bytes.Buffer{}
tea := io.TeeReader(c.Request.Body, buf)
body, err := ioutil.ReadAll(tea)

总结

这篇文章从参数绑定的问题出发,为大家介绍了两种组合参数的小技巧,提供了使用参数绑定的时候的一个建议,并且提出了非官方,侵入性小的的多次读取 request body 的方式。

相关推荐

一个基于.Net Core遵循Clean Architecture原则开源架构

今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...

AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%

写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...

OneCode低代码平台的事件驱动设计:架构解析与实践

引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...

国内大厂AI插件评测:根据UI图生成Vue前端代码

在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...

AI+低代码技术揭秘(二):核心架构

本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...

GitDiagram用AI把代码库变成可视化架构图

这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...

30天自制操作系统:第六天:代码架构整理与中断处理

1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...

AI写代码越帮越忙?2025年研究揭露惊人真相

近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...

一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具

一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...

5分钟掌握 c# 网络通讯架构及代码示例

以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...

从复杂到优雅:用建造者和责任链重塑代码架构

引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...

低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈

专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...

框架设计并不是简单粗暴地写代码,而是要先弄清逻辑

3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...

大佬用 Avalonia 框架开发的 C# 代码 IDE

AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...

轻量级框架Lagent 仅需20行代码即可构建自己的智能代理

站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...

取消回复欢迎 发表评论: