Gin的基本使用 | 青训营笔记

328 阅读4分钟

这是我参与「第五届青训营 」笔记创作活动的第2天。

在学习hertz时,发现对go原本的http框架并不是很熟悉,所以参考了视频学习了gin,有了对http框架更加形象的认识

1. 安装

  1. gin 的的依赖,使用 go get 获取
$ go get -u github.com/gin-gonic/gin
  1. 使用 go mod 来管理项目
go mod init gin-demo
  1. 在执行文件 main.go 中引入 gin 以及 http 包
import (  
   "github.com/gin-gonic/gin"  
   "net/http"    // 用于使用诸如 `http.StatusOK` 之类的常量
)

2. 启动

以一个get请求为例,启动一个web后端的步骤主要分为三步:

  • 使用 gin.Default() 初始化一个带有默认中间件的web引擎:server
  • 构造 get 请求
  • 使用 server.run 启动服务,并且开始监听路由,此时如果传入:xxxx,则服务的端口号将变为xxxx(默认是8080端口)
package main

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

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

3. web开发中常用

3.1 Restful API

gin的引擎提供了四大类型的请求方式,可以使用类似于get请求的方式构造其他请求方式的请求。使用Restful API的方式,使用用一个一个路由即可以代表多种 method 的请求。

  • server.GET("/user", ...)
  • server.POST("/user", ...)
  • server.PUT("/user", ...)
  • server.DELETE("/user", ...)

3.2 响应页面

前后端之间没有用json数据传递时,请求中会响应页面,此时需要将html页面返回出去,并且加上需要附加的数据。

  1. 首先设置html文件的位置
server.LoadHTMLGlob("./templates/*")
  1. 随后在请求中使用 HTML() 方法,设置当前响应的页面名称以及相应的数据
server.GET("/index", func(context *gin.Context) {  
   context.HTML(http.StatusOK, "index.html", gin.H{"msg": "这是一个首页"})  
})
  1. 在静态文件中使用模板的语法写入需要填充的数据
<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Title</title>  
</head>  
<body>  
<p>{{.msg}}</p>  
</body>  
</html>

404页面的响应

404为找不到路由,所以只要调用 NoRoute 方法即可。

server.NoRoute(func(context *gin.Context) {  
   context.HTML(http.StatusNotFound, "404.html", nil)  
})

3.3 获取url中传参

3.3.1 Query方式

当路由使用的是 url?param1=xxx&param2=yyy 时,使用 context.Query 方法获取对应key的value。

server.POST("/user", func(context *gin.Context) {  
   username := context.Query("username")  
   passwd := context.Query("passwd")  
   context.JSON(http.StatusOK, gin.H{  
      "username": username,  
      "passwd":   passwd,  
   })  
})

3.3.2 Param方式

当传递的参数就在路由中时,可以使用 context.Param() 方法获取,此时需要在请求的路由中加上 :*,两个字符的意义的区别如下:

  • "user/:name":该参数需要严格匹配到name,如果只有"user(/)",并没有后序的字符串,则无法匹配该路由
  • "user/:name/*action":*可以理解为任意字符串,包括空字符串,他可以匹配"user/awsling/abc"。当action处的字符串为空时,也可以匹配,匹配到的路由是"user/awsling/",此时action对应的参数是"/"。(action匹配的字符串会把"/"加上)
server.POST("/user/:username/*action", func(context *gin.Context) {  
   username := context.Param("username")  
   action := context.Param("action")  
   context.JSON(http.StatusOK, gin.H{  
      "username": username,  
      "action":   action,  
   })  
}

3.3.3 获取request中的body

当前端传递的是json数据,一般都是从body中传过来的,此时需要从context中获取body数据,此时从body中拿出的是byte数组,即序列化过的结果,需要将其反序列化到map中,在处理请求。

server.POST("/user/login", func(context *gin.Context) {  
   data, err := context.GetRawData()  
   if err != nil {  
      log.Println(err.Error())  
      return  
   }  
   
   var m map[string]interface{}  
   if err := json.Unmarshal(data, &m); err != nil {  
      log.Println(err.Error())  
      return  
   }  
   context.JSON(http.StatusOK, m)  
})

3.3.4 获取表单中的数据

当前后端不分离时,提交的表单通过form的形式传递过来,此时需要使用 server.PostForm() 方法获取表单的数据

server.POST("/user/register", func(context *gin.Context) {  
   username := context.PostForm("username")  
   passwd := context.PostForm("passwd")  
   context.JSON(http.StatusOK, gin.H{  
      "username": username,  
      "passwd":   passwd,  
   })  
})

3.4 路由管理

由于路由到后面会越来越多,所以需要将路由进行分组,以对路由进行分类管理。 使用 server.Group("/xxx") 为 xxx 分配路由组,随后注册路由到路由组中。

userGroup := server.Group("/user")  
{  
   userGroup.GET("/info")  
   userGroup.POST("/login")  
   userGroup.POST("/register")  
}

orderGroup := server.Group("/order")  
{  
   orderGroup.GET("/info")  
   orderGroup.POST("/info")  
}

3.5 使用中间件

中间件可以看做是请求的拦截处理器,用于处理请求前,请求中,请求后的一系列操作。 gin.Default() 默认会带有 logger、recover 两个中间件;如果不想使用原始带有的中间件,则需要使用 gin.New() 创建服务。

配置中间件有三种方法:

  • 全局中间件,所有的请求都会使用该中间件。
server.Use(handler()):
  • 路由配置中间件,对于每个路由,可以配置任意数量的中间件
server.GET("/benchmark", MyBenchLogger(), ...)
  • 路由组配置中间件,有两种方式
// 在此例中,我们在 "authorized" 路由组中使用自定义创建的 
// AuthRequired() 中间件
// 一步完成
authorized := r.Group("/", AuthRequired())
// 上一句代码和使用以下两行代码的效果完全一样:
authorized := r.Group("/")
Authorized.Use(AuthRequired())