当前位置: 首页 > article >正文

【Go学习】04-1-Gin框架-路由请求响应参数

【Go学习】04-1-Gin框架

  • 初识框架
    • go流行的web框架
      • Gin
      • iris
      • Beego
      • fiber
    • Gin介绍
    • Gin快速入门
  • 路由
    • RESTful API规范
    • 请求方法
    • URI
      • 静态url
      • 路径参数
      • 模糊匹配
    • 处理函数
    • 分组路由
  • 请求参数
    • GET请求参数
      • 普通参数
      • 数组参数
      • map参数
    • POST请求参数
      • 表单参数
      • JSON参数
    • 路径参数
    • 文件参数
  • 响应
    • 字符串方式
    • JSON方式
    • XML方式
    • 文件方式
    • 设置http响应头
    • 重定向
    • YAML方式


初识框架

框架是一系列工具的集合,能让开发变的便捷。

学习框架的目的就是为了提供项目的开发效率,使我们更加专注业务,而不是和业务无关的底层代码。

go流行的web框架

如果学习过其他语言,可能知道Java用的比较多的是Spring框架,PHP用的比较多的是Laravel,python用的多的是Django,都在各自的语言中具有强大的统治力。

go从诞生之初就带有浓重的开源属性,其原生库已经很强大,即使不依赖框架,也能进行高性能开发,又因为其语言并没有一定的设计标准,所以较为灵活,也就诞生了众多的框架,各具有特色,满足不同的喜好。

Gin

地址:https://github.com/gin-gonic/gin

号称最快的go语言web框架,目前是go官方的推荐框架(https://go.dev/doc/tutorial/)。

iris

地址:https://github.com/kataras/iris

性能比gin高一些,支持MVC,但这款框架评价不太好,使用上问题较多,近些年很少去选择使用

Beego

地址:https://github.com/beego/beego

国人开发,最早的go web框架之一,工具集比较完善,性能较差,据传言作者是php转行,所以框架带有浓厚的php特色,早期国内使用的多,目前少有人选择。

fiber

地址:https://github.com/gofiber/fiber

2020年发布的框架,发展迅速,建立在fasthttp之上,性能目前最高,受Express启发,比较简洁,上手较快,和gin类似。

当然还有其他一些框架,但从star数上,以及流行程度上看,gin一骑绝尘,gin的好处在于其简洁,扩展性,稳定性以及性能都比较出色。

go的框架其实是可以理解为库,并不是用了某一个框架就不能用别的框架,可以选择性的使用各个库中的优秀组件,进行组合

Gin介绍

特性:

  • 快速

    基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。

  • 支持中间件

    传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。

  • Crash 处理

    Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

  • JSON 验证

    Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。

  • 路由组

    更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。

  • 错误管理

    Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。

  • 内置渲染

    Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

  • 可扩展性

    新建一个中间件非常简单。

Gin快速入门

go版本需求:go1.13及以上

环境:windows 11

# 创建工作区
F:\Code\Golang\TuLing\workPath>mkdir ginlearn
F:\Code\Golang\TuLing\workPath>cd ginlearn
# 初始化工作区
F:\Code\Golang\TuLing\workPath\ginlearn>go work init

# 创建模块
F:\Code\Golang\TuLing\workPath\ginlearn>mkdir helloworld
F:\Code\Golang\TuLing\workPath\ginlearn>cd helloworld
# 初始化模块
F:\Code\Golang\TuLing\workPath\ginlearn\helloworld>go mod init test.com/helloworld
go: creating new go.mod: module test.com/helloworld
F:\Code\Golang\TuLing\workPath\ginlearn\helloworld>cd ..

# 将模块加入工作区
F:\Code\Golang\TuLing\workPath\ginlearn>go work use ./helloworld

使用goland打开

在这里插入图片描述

下载gin

PS F:\Code\Golang\TuLing\workPath\ginlearn> cd .\helloworld\
PS F:\Code\Golang\TuLing\workPath\ginlearn\helloworld> go get -u github.com/gin-gonic/gin

示例程序,创建main.go

package main

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

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

运行后,apifox进行测试

在这里插入图片描述

符合预期,这样简单的代码就实现了一个http的服务

路由

路由是URI到函数的映射。

一个URI含: http://localhost:8080/user/find?id=11

  • 协议,比如http,https等
  • ip端口或者域名,比如127.0.0.1:8080或者www.test.com
  • path,比如 /path
  • query,比如 ?query

同时访问的时候,还需要指明HTTP METHOD,比如

  • GET

    GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.

  • POST

    POST方法用于将实体提交到指定的资源,通常会导致在服务器上的状态变化

  • HEAD

    HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体.

  • PUT

    PUT方法用请求有效载荷替换目标资源的所有当前表示

  • DELETE

    DELETE方法删除指定的资源

  • CONNECT

    CONNECT方法建立一个到由目标资源标识的服务器的隧道。

  • OPTIONS

    OPTIONS方法用于描述目标资源的通信选项。

  • TRACE

    TRACE方法沿着到目标资源的路径执行一个消息环回测试。

  • PATCH

    PATCH方法用于对资源应用部分修改。

使用的时候,应该尽量遵循其语义

RESTful API规范

RESTful API 的规范建议我们使用特定的HTTP方法来对服务器上的资源进行操作。

比如:

  1. GET,表示读取服务器上的资源
  2. POST,表示在服务器上创建资源
  3. PUT,表示更新或者替换服务器上的资源
  4. DELETE,表示删除服务器上的资源
  5. PATCH,表示更新/修改资源的一部分

请求方法

r.GET("/get", func(ctx *gin.Context) {
	ctx.JSON(200, "get")
})
r.POST("/post", func(ctx *gin.Context) {
	ctx.JSON(200, "post")
})
r.DELETE("/delete", func(ctx *gin.Context) {
	ctx.JSON(200, "delete")
})
r.PUT("/put", func(ctx *gin.Context) {
	ctx.JSON(200, "put")
})

如果想要支持所有:

r.Any("/any", func(ctx *gin.Context) {
    ctx.JSON(200, "any")
})

如果想要支持其中的几种:

r.GET("/hello", func(ctx *gin.Context) {
    //数组 map list 结构体
    ctx.JSON(200, gin.H{
       "name": "hello world",
    })
})
r.POST("/hello", func(ctx *gin.Context) {
    //数组 map list 结构体
    ctx.JSON(200, gin.H{
       "name": "hello world",
    })
})

URI

URI书写的时候,我们不需要关心scheme和authority这两部分,我们主要通过path和query两部分的书写来进行资源的定位。

静态url

比如/hello/user/find

r.POST("/user/find", func(ctx *gin.Context) {
})

路径参数

比如/user/find/:id

r.POST("/user/find/:id", func(ctx *gin.Context) {
    param := ctx.Param("id")
    ctx.JSON(200, param)
})

模糊匹配

比如/user/*path

r.POST("/user/*path", func(ctx *gin.Context) {
		param := ctx.Param("path")
		ctx.JSON(200, param)
})

处理函数

定义:

type HandlerFunc func(*Context)

通过上下文的参数,获取http的请求参数,响应http请求等。

分组路由

在进行开发的时候,我们往往要进行模块的划分,比如用户模块,以user开发,商品模块,以goods开头。

或者进行多版本开发,不同版本之间路径是一致的,这种时候,就可以用到分组路由了。

比如

ug := r.Group("/user")
{
    ug.GET("find", func(ctx *gin.Context) {
       ctx.JSON(200, "user find")
    })
    ug.POST("save", func(ctx *gin.Context) {
       ctx.JSON(200, "user save")
    })
}
gg := r.Group("/goods")
{
    gg.GET("find", func(ctx *gin.Context) {
       ctx.JSON(200, "goods find")
    })
    gg.POST("save", func(ctx *gin.Context) {
       ctx.JSON(200, "goods save")
    })
}

请求路径则为

[GIN-debug] GET    /user/find                --> main.main.func2 (3 handlers)
[GIN-debug] POST   /user/save                --> main.main.func3 (3 handlers)
[GIN-debug] GET    /goods/find               --> main.main.func4 (3 handlers)
[GIN-debug] POST   /goods/save               --> main.main.func5 (3 handlers)

请求参数

GET请求参数

使用Get请求传参时,类似于这样

http://localhost:8080/user/save?id=11&name=zhangsan

如何获取呢?

普通参数

request url: http://localhost:8080/user/save?id=11&name=zhangsan

  • Query:匹配字段

    r.GET("/user/save", func(ctx *gin.Context) {
        id := ctx.Query("id")
        name := ctx.Query("name")
        ctx.JSON(200, gin.H{
           "id":   id,
           "name": name,
        })
    })
    

    如果参数不存在,就给一个默认值:

  • DefaultQuery:query为空时回返回个默认值

    r.GET("/user/save", func(ctx *gin.Context) {
        id := ctx.Query("id")
        name := ctx.Query("name")
        address := ctx.DefaultQuery("address", "北京")
        ctx.JSON(200, gin.H{
           "id":      id,
           "name":    name,
           "address": address,
        })
    })
    
  • GetQuery:多了个query成功与否的返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        id, ok := ctx.GetQuery("id")
        address, aok := ctx.GetQuery("address")
        ctx.JSON(200, gin.H{
           "id":      id,
           "idok":    ok,
           "address": address,
           "aok":     aok,
        })
    })
    

    id是数值类型,上述获取的都是string类型,根据类型获取:通过form进行字段匹配

  • BindQuery:与结构体字段进行匹配

    type User struct {
    	Id   int64  `form:"id"`
    	Name string `form:"name"`
    }
    r.GET("/user/save", func(ctx *gin.Context) {
    		var user User
    		err := ctx.BindQuery(&user)
    		if err != nil {
    			log.Println(err)
    		}
    		ctx.JSON(200, user)
    })
    
  • ShouldBindQuery:有binding字段的要求必填,否则报错

    r.GET("/user/save", func(ctx *gin.Context) {
        var user User
        err := ctx.ShouldBindQuery(&user)
        if err != nil {
           log.Println(err)
        }
        ctx.JSON(200, user)
    })
    

    区别

    当bind是必须的时候,ShouldBindQuery会报错,开发者自行处理,状态码不变。

    type User struct {
        Id      int64  `form:"id"`
        Name    string `form:"name"`
        Address string `form:"address" binding:"required"`
    }
    

    BindQuery则报错的同时,会将状态码改为400。所以一般建议是使用Should开头的bind。

数组参数

请求url:http://localhost:8080/user/save?address=Beijing&address=shanghai

  • QueryArray:重复查询字段组装成数组

    r.GET("/user/save", func(ctx *gin.Context) {
        address := ctx.QueryArray("address")
        ctx.JSON(200, address)
    })
    
  • GetQueryArray:多成功与否返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        address, ok := ctx.GetQueryArray("address")
        fmt.Println(ok)
        ctx.JSON(200, address)
    })
    
  • ShouldBindQuery

    r.GET("/user/save", func(ctx *gin.Context) {
        var user User
        err := ctx.ShouldBindQuery(&user)
        fmt.Println(err)
        ctx.JSON(200, user)
    })
    

    但是这样的话我们的user的address要求就是个数组

    type User struct {
        Id      int64    `form:"id"`
        Name    string   `form:"name"`
        Address []string `form:"address" binding:"required"`
    }
    

    成功返回

    {
        "Id": 0,
        "Name": "",
        "Address": [
            "Beijing",
            "shanghai"
        ]
    }
    

map参数

请求url:http://localhost:8080/user/save?addressMap[home]=Beijing&addressMap[company]=shanghai

  • QueryMap:组装成map

    r.GET("/user/save", func(ctx *gin.Context) {
        addressMap := ctx.QueryMap("addressMap")
        ctx.JSON(200, addressMap)
    })
    
  • GetQueryMap:多成功与否返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        addressMap, _ := ctx.GetQueryMap("addressMap")
        ctx.JSON(200, addressMap)
    })
    

    返回值

    {
        "company": "shanghai",
        "home": "Beijing"
    }
    

POST请求参数

post请求一般是表单参数和json参数

表单参数

r.POST("/user/save", func(ctx *gin.Context) {
    id := ctx.PostForm("id")
    name := ctx.PostForm("name")
    address := ctx.PostFormArray("address")
    addressMap := ctx.PostFormMap("addressMap")
    ctx.JSON(200, gin.H{
       "id":         id,
       "name":       name,
       "address":    address,
       "addressMap": addressMap,
    })
})
  • PostForm:从表单中对应的字段
  • PostFormArray:从表单找对应的数组
  • PostFormMap:从表单找对应的Map
r.POST("/user/save", func(ctx *gin.Context) {
    var user User
    err := ctx.ShouldBind(&user)
    addressMap, _ := ctx.GetPostFormMap("addressMap")
    user.AddressMap = addressMap
    fmt.Println(err)
    ctx.JSON(200, user)
})
  • GetPostFormMap:从表单找对应的Map

JSON参数

json参数如下

{
    "id":1111,
    "name":"zhangsan",
    "address": [
        "beijing",
        "shanghai"
    ],
    "addressMap":{
        "home":"beijing"
    }
}
r.POST("/user/save", func(ctx *gin.Context) {
    var user User
    err := ctx.ShouldBindJSON(&user)
    fmt.Println(err)
    ctx.JSON(200, user)
})

对应字段进行匹配

其他类型参数注入xml,yaml等和json道理一样

路径参数

请求url:http://localhost:8080/user/save/111

r.POST("/user/save/:id", func(ctx *gin.Context) {
    ctx.JSON(200, ctx.Param("id"))
})
  • :id 表示 占位符,可以匹配 任意路径中的值

  • ctx.Param("id") 用于 获取路径参数 id 的值

文件参数

r.POST("/user/save", func(ctx *gin.Context) {
    form, err := ctx.MultipartForm()
    if err != nil {
       log.Println(err)
    }
    files := form.File
    for _, fileArray := range files {
       for _, v := range fileArray {
          ctx.SaveUploadedFile(v, "./"+v.Filename)
       }

    }
    ctx.JSON(200, form.Value)
})

在form表单中请求file类型

在这里插入图片描述

这样就能在本地看到了

在这里插入图片描述

响应

字符串方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.String(http.StatusOK, "this is a %s", "ms string response")
})

JSON方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.JSON(http.StatusOK, gin.H{
       "success": true,
    })
})

XML方式

r.GET("/user/save", func(ctx *gin.Context) {
    u := XmlUser{
       Id:   11,
       Name: "zhangsan",
    }
    ctx.XML(http.StatusOK, u)
})

文件方式

r.GET("/user/save", func(ctx *gin.Context) {
    //ctx.File("./1.png")
    ctx.FileAttachment("./1.png", "2.png")
})

设置http响应头

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.Header("test", "headertest")
})

重定向

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
})

YAML方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.YAML(200, gin.H{"name": "ms", "age": 19})
})

http://www.kler.cn/a/583227.html

相关文章:

  • sql server 2016 版本补丁说明
  • 颤抖:quiver,shiver,tremble的区别
  • SQL注入之sqli-labs靶场搭建
  • Linux 字符设备驱动实例
  • wpf中DataGrid组件每一行的背景色动态变化
  • 纯前端全文检索的两种实现方案:ElasticLunr.js 和 libsearch
  • 网络通信Socket中多态HandleIO设计模式深度解析
  • 打造Windows服务器安全堡垒:安当SLA双因素认证方案详解
  • Docker 入门与实战指南
  • PostgreSQL与Oracle数据库相比有什么优势
  • sql靶场--布尔盲注(第八关)保姆级教程
  • Flash Attention 算法简介
  • openai-cua-sample-app - 使用计算机的 Agent示例应用
  • 【C语言系列】字符函数和字符串函数
  • golang算法二分查找
  • Qt开源控件库(qt-material-widgets)的编译及使用
  • 【原创】springboot+vue音乐教育培训管理系统设计与实现
  • 2.angular指令
  • AI驱动的数字供应链安全情报预警服务:云脉XSBOM
  • Token登录授权、续期和主动终止的方案(Redis+Token(非jwtToken))