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
}
}
后端向前端发送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)
})
方式一就是通过map封装的数据,map的value类型是一个空接口类型,可以接收任意类型数据;
r.GET("/json02", func(c *gin.Context) {
data := gin.H{
"name": "张三",
"gender": "男",
"age": 66,
}
c.JSON(http.StatusOK, data)
})
第二种方法是使用的gin.H{},可以看一下H是什么:
可以看到H本质上就是一个map[string]interface{},和第一种方法是一样的;
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)
})
这种方法是封装了一个响应结果的结构体,并设置响应json的参数:json:"name"
,这样返回结果就是:“name”: “小三”
这里注意,如果不设置响应的json参数,那么返回的json为:“Name”: “小三”,key值默认为结构体对应属性;
如果把结构体属性改为首字母小写,如:name string,那么该属性不会发送到前端,得到的json响应结果就为空;
总之,记住一点:结构体中属性首字母大写,如果想要自定义返回Json的key值可以用tag;这里有个文章介绍结构体Json映射:传送门
发送Json数据都是使用的c.JSON(状态码, 响应结果数据),记住这种形式即可;
在实际项目中一般为了规范会使用第三种方法,封装一个xxxxResponse结构体封装响应结果;
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,
})
})
通过Query获取querystring,传入key,返回value
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,
})
})
通过DefaultQuery获取querystring,传入key,如果key存在返回value,如果key不存在返回defaultValue默认值;
如果接收的key不存在:
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,
})
})
通过GetQuery获取querystring,传入key,如果key存在返回(value, true),如果key不存在返回(“”, false)
如果接收的key不存在:
控制台输出:
这三种方法根据实际情况使用即可,当然也可以选择其他接收querystring参数的方式:
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,
})
})
就是用到了和Get请求相似的方法:
都一样,不过多介绍;
一般请求url为这样:127.0.0.1:8080/info/张三/78
/张三/78就是发送到后端的参数,这种方式和querystring是有细微区别的;
Java的SpringMVC框架处理方式是/{参数名1}/{参数名2},然后加上@PathVariable注解:
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,
})
})
这里使用Param()来接收参数;
注意路径里这些动态参数写法,是/:参数名1/:参数名2,这种方法和Vue中的路由传参很像;
当前端发送一个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,
})
}
})
java中是映射到对应的类中(一般会自定义一个请求类xxxRequest),这里就是结构体,一般命名也是xxxRequest:
结构体需要加上form标签,这样ShouldBind才能和该结构体字段一一对应;
同样注意ShouldBind中传入地址,不然值传递无法赋值成功;
前端发送Json数据给后端在前后端分离项目中是非常常见的,一般前端封装好数据后直接一个Json发给后端,所以这个方法是很常用的;
同样,这个方法也可以接收form类型参数:
路由在很多地方都可以见到;在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) {...})
.....
有两种特殊的:
Any:了解就行,一般不用;
// Any可以接收所有类型的请求(get/post/put/delete...)
r.Any("/any", func(c *gin.Context) {...})
一般我们会把不存在的访问页面设置为404,如何把所有不存在的页码设置成404呢?
可以通过NoRoute实现:
r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "/404.html", nil)})
注:一般前后端分离项目中前端项目就会做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")
}
路由组在项目中很常用,可以把路由单独作为一个组件来用:
这只是一个例子,可以参考;
该文章仅用于个人笔记总结;