• Gin——请求发送和路由总结


    引言

    Gin是Golang的一个web框架,性能高且拓展性强,使用也很简单;

    下面简单介绍一些请求时常用到的操作;

    注:当下前后端分离项目居多,所以对于Gin框架模板相关的内容就不介绍了;

    下面所有代码省略以下内容,只保留请求发送过程:

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func main() {
    	r := gin.Default()
        
        ......
        请求发送
        ......
        
    	err := r.Run(":8080")
    	if err != nil {
    		err.Error()
    		return
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    后端响应JSON数据

    后端向前端发送Json数据时最常用的前后端交互方式;

    Java中的SpringMVC框架默认返回是走视图解析器的,如果想要返回Json类型数据会用到@ResponseBody或者@RestController注解;

    下面三种方法本质是一样的(区别就在响应数据封装的方法上),都很简单:

    方式一

    r.GET("/json01", func(c *gin.Context) {
    	data := map[string]interface{}{
    		"name":   "小冰",
    		"gender": "女",
    		"age":    22,
    	}
    	c.JSON(http.StatusOK, data)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    方式一就是通过map封装的数据,map的value类型是一个空接口类型,可以接收任意类型数据;

    image-20220710100745576

    方式二

    r.GET("/json02", func(c *gin.Context) {
    	data := gin.H{
    		"name":   "张三",
    		"gender": "男",
    		"age":    66,
    	}
    	c.JSON(http.StatusOK, data)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第二种方法是使用的gin.H{},可以看一下H是什么:

    image-20220710093932708

    可以看到H本质上就是一个map[string]interface{},和第一种方法是一样的;

    image-20220710100709606

    方式三

    type msg struct {
    	Name   string `json:"name"`
    	Gender string `json:"gender"`
    	Age    int    `json:"age"`
    }
    r.GET("/json03", func(c *gin.Context) {
    	data := msg{"小三", "男", 44}
    	c.JSON(http.StatusOK, data)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这种方法是封装了一个响应结果的结构体,并设置响应json的参数:json:"name",这样返回结果就是:“name”: “小三”

    这里注意,如果不设置响应的json参数,那么返回的json为:“Name”: “小三”,key值默认为结构体对应属性;

    如果把结构体属性改为首字母小写,如:name string,那么该属性不会发送到前端,得到的json响应结果就为空;

    总之,记住一点:结构体中属性首字母大写,如果想要自定义返回Json的key值可以用tag;这里有个文章介绍结构体Json映射:传送门

    image-20220710100826291


    发送Json数据都是使用的c.JSON(状态码, 响应结果数据),记住这种形式即可;

    在实际项目中一般为了规范会使用第三种方法,封装一个xxxxResponse结构体封装响应结果;

    接收前端querystring参数(GET)

    querystring参数其实就是前端传来的key=value形式数据,很常见,比如:127.0.0.1:8080/query?name=张三&age=78

    这里name=张三和age=78就是key=value形式数据,一般常用于后端Get查询数据;

    Java的springMVC框架是直接就可以接收这些数据(前提名称对应),一般使用@RequerParam()获取参数;

    Gin框架有很多种方法接收querystring参数,这里介绍三种常用的:

    方式一

    r.GET("/query01", func(c *gin.Context) {
    	name := c.Query("name")
    	age := c.Query("age")
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过Query获取querystring,传入key,返回value

    image-20220710102247665

    方式二

    r.GET("/query02", func(c *gin.Context) {
    	name := c.DefaultQuery("name", "姓名为空")
    	age := c.DefaultQuery("age", "年龄为空")
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过DefaultQuery获取querystring,传入key,如果key存在返回value,如果key不存在返回defaultValue默认值;

    如果接收的key不存在:

    image-20220710102343545

    方式三

    r.GET("/query03", func(c *gin.Context) {
    	name, okName := c.GetQuery("name")
    	if !okName {
    		fmt.Println("查询条件name为空")
    	}
    	age, okAge := c.GetQuery("age")
    	if !okAge {
    		fmt.Println("查询条件age为空")
    	}
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通过GetQuery获取querystring,传入key,如果key存在返回(value, true),如果key不存在返回(“”, false)

    如果接收的key不存在:

    image-20220710102426874

    控制台输出:

    image-20220710102447402


    这三种方法根据实际情况使用即可,当然也可以选择其他接收querystring参数的方式:

    image-20220710095732732

    接收前端form参数(POST)

    form类型参数一般就是表单提交什么的,后端发送的是一个Post请求,其实和接收querystring差不多,方法也相似,就不过多介绍了;

    r.POST("/submit01", func(c *gin.Context) {
    	name := c.PostForm("name")
    	age := c.PostForm("age")
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    r.POST("/submit02", func(c *gin.Context) {
    	name := c.DefaultPostForm("name", "姓名为空")
    	age := c.DefaultPostForm("age", "年龄为空")
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    r.POST("/submit03", func(c *gin.Context) {
    	name, okName := c.GetPostForm("name")
    	if !okName {
    		fmt.Println("查询条件name为空")
    	}
    	age, okAge := c.GetPostForm("age")
    	if !okAge {
    		fmt.Println("查询条件age为空")
    	}
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    就是用到了和Get请求相似的方法:

    • PostForm()
    • DefaultPostForm()
    • GetPostForm()

    都一样,不过多介绍;

    image-20220710115531446

    接收Restful风格参数(GET)

    一般请求url为这样:127.0.0.1:8080/info/张三/78

    /张三/78就是发送到后端的参数,这种方式和querystring是有细微区别的;

    Java的SpringMVC框架处理方式是/{参数名1}/{参数名2},然后加上@PathVariable注解:

    image-20220710132837014

    gin框架是通过函数获取的参数,先看一下代码:

    r.GET("/restful01/:name/:age", func(c *gin.Context) {
    	name := c.Param("name") // 用Param()接收参数
    	age := c.Param("age")
    	c.JSON(http.StatusOK, gin.H{
    		"name": name,
    		"age":  age,
    	})
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里使用Param()来接收参数;

    注意路径里这些动态参数写法,是/:参数名1/:参数名2,这种方法和Vue中的路由传参很像;

    image-20220710133223719

    参数绑定(POST)

    当前端发送一个Json类型数据时,后端该如何接收?

    在Java的springMvc中有一个@RequestBody注解可以解析Json数据和对象的属性一一对应,go中也有相似的操作,这里只介绍一种方式,使用ShouldBind()函数;(还有其他几种函数,功能类似只有细微区别)

    举个例子:

    type People struct {
    	Name string `json:"name" form:"name"`
    	Age  int    `json:"age" form:"age"`
    }
    r.POST("/json", func(c *gin.Context) {
    	var people People
    	if err := c.ShouldBind(&people); err != nil { // 注意传入地址,这样才能赋值成功
    		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    	} else {
    		c.JSON(http.StatusOK, gin.H{
    			"people": people,
    		})
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220710175418353

    java中是映射到对应的类中(一般会自定义一个请求类xxxRequest),这里就是结构体,一般命名也是xxxRequest:

    image-20220710175913045

    结构体需要加上form标签,这样ShouldBind才能和该结构体字段一一对应;

    同样注意ShouldBind中传入地址,不然值传递无法赋值成功;


    前端发送Json数据给后端在前后端分离项目中是非常常见的,一般前端封装好数据后直接一个Json发给后端,所以这个方法是很常用的;

    同样,这个方法也可以接收form类型参数:

    image-20220710175319415

    路由

    路由在很多地方都可以见到;在gin中恰当的使用路由,可以使代码更利于管理,下面简单介绍一下路由;

    路由类别

    路由主要有以下几种(其实上面写的请求发送就是路由):

    r.GET("/get", func(c *gin.Context) {...})
    r.PUT("/put", func(c *gin.Context) {...})
    r.POST("/post", func(c *gin.Context) {...})
    r.DELETE("/delete", func(c *gin.Context) {...})
    .....
    
    • 1
    • 2
    • 3
    • 4
    • 5

    有两种特殊的:

    Any:了解就行,一般不用;

    // Any可以接收所有类型的请求(get/post/put/delete...)
    r.Any("/any", func(c *gin.Context) {...})
    
    • 1
    • 2

    一般我们会把不存在的访问页面设置为404,如何把所有不存在的页码设置成404呢?

    可以通过NoRoute实现:

    r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "/404.html", nil)})
    
    • 1

    注:一般前后端分离项目中前端项目就会做404页面的设置,后端只需要数据交互即可,所以这个方法在前后端分离项目也不会多用;

    路由组

    路由组在项目中很实用,意思就是把一组路由分为一组,其实我理解为一个路由组中的接口就类似Java中一个Controller内的接口;

    通过路由组可以将相同url前缀的路由归为一类:

    func main() {
    	r := gin.Default()
        // 路由可以使用{}括起来,这样分组更清晰,当然不括也可以
    	userGroup := r.Group("/index")
    	{
    		userGroup.GET("/login", func(c *gin.Context) {...})
    		userGroup.POST("/register", func(c *gin.Context) {...})
    
    	}
        // 路由组也可以嵌套,这里url前缀就是:/index/
    	playGroup := userGroup.Group("/shop")
    	{
    		playGroup.GET("/game", func(c *gin.Context) {...})
    		playGroup.GET("/music", func(c *gin.Context) {...})
    	}
        r.Run(":9000")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    路由组在项目中很常用,可以把路由单独作为一个组件来用:

    image-20220711011147581

    这只是一个例子,可以参考;


    该文章仅用于个人笔记总结;

  • 相关阅读:
    函数栈帧的创建和销毁
    WX小程序的常用知识点总结
    Oracle/PLSQL: Avg Function
    Text2Cypher:大语言模型驱动的图查询生成
    HTTP长连接
    Alter database open fails with ORA-00600 kcratr_nab_less_than_odr
    【css面试题】实现2栏布局 右侧自适应; 3栏布局 中间自适应
    自动驾驶中间件
    出现Browse information of one xxxx解决方法
    2021年InfoWorld 精选最佳开源软件
  • 原文地址:https://blog.csdn.net/YXXXYX/article/details/125713689