gin 框架介绍和使用

1 安装使用

  • 下载安装
$ go get -u github.com/gin-gonic/gin
  • 使用事例
package main

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

func main() {
	GinObj := gin.Default() // 创建默认的路由引擎
	GinObj.GET("/book", get) // get方法,第一个参数是url,第二个参数是调用的函数
	GinObj.GET("/struct", structFunc)
  GinObj.Run() // 启动http服务,默认是0.0.0.0:8080启动服务;可以跟字符串参数,自定义启动服务的端口:":9090"
}

func get(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "get book",
	}) // 返回json数据,第一个参数是状态码,第二个参数是空接口类型,也就是任意数据类型
}

func structFunc(c *gin.Context) {
	var user struct{
		Name string
		Age int
	}
	user.Name = "lynn"
	user.Age = 18
	c.JSON(200, "user")
}

遇到的坑

2 API restful

以请求方法,表示不同的处理逻辑,且url都用名词,结果是json格式数据

gin遵循api restful规范:

​ 路由引擎对象的方法:.GET查询数据,对应get请求、POST创建数据,对应post方法、PUT更新数据,put方法、DELETE删除数据,delete方法

package main

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

func main() {
	GinObj := gin.Default()
	GinObj.GET("/book", get) // get请求,查询
	GinObj.POST("/book", get) // post请求,创建
	GinObj.PUT("/book", get) // put请求,修改。提交所有数据
	GinObj.PATCH("/book", get) // patch请求,修改。只提交修改的数据
	GinObj.DELETE("/book", get) // delete请求,删除数据
  GinObj.Run() // 可以指定端口,例子: ":9090"
}

func get(c *gin.Context) {
	responseData := map[string]string{
		"message": "success",
	}
	c.JSON(200, responseData) // 200是状态码,第二个参数是返回值
}

3 获取参数

01 通过url传参

get请求时,在url之后以?name=lynn&age=18这种格式传参数

package main

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

func main() {
	GinObj := gin.Default()
	GinObj.GET("/book", get)
	GinObj.Run()
}

func get(c *gin.Context) {
	name := c.Query("name") // 没传值时,结果为空字符串
  age := c.DefaultQuery("age", "18") // 没传值时,结果为: "18"
	responseData := map[string]interface{}{
		"message": "success",
	}
	c.JSON(200, responseData)
}

注意:

Query:只能有一个参数,要接收数据的key值,key不存在时,默认为空字符串

DefaultQuery:有两个参数,第一个时要接收数据的key值,第二个是key不存在时,默认的值。注意key存在,value为空字符串时不默认

02 form表单参数

通过form表单提交数据时,post、patch等

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

var responseData = map[string]interface{}{
	"message": "success",
}

func main() {
	GinObj := gin.Default()
	GinObj.POST("/book", post)
	GinObj.Run()
}

func post(c *gin.Context) {
	name := c.PostForm("name")
	age := c.DefaultPostForm("age", "90") //
	responseData["name"] = name
	responseData["age"] = age
	fmt.Println(name)
	fmt.Println(age)
	c.JSON(200, responseData)
}

注意:

PostForm:只能有一个参数,要接收数据的key值,key不存在时,默认为空字符串

DefaultPostForm:有两个参数,第一个时要接收数据的key值,第二个是key不存在时,默认的值。注意key存在,value为空字符串时不默认

03 json数据

前后端分离的项目,一般是json数据

package main

import (
	"encoding/json"
	"github.com/gin-gonic/gin"
)

var responseData = map[string]interface{}{
	"message": "success",
}

func main() {
	GinObj := gin.Default()
	GinObj.POST("/book", post)
	GinObj.Run()
}

}

func post(c *gin.Context) {
	data, _ := c.GetRawData()
	var b map[string]interface{}
	_ = json.Unmarshal(data, &b) // 如果是map记得用指针,因为map是值类型
	responseData["data"] = b
	c.JSON(200, responseData)
}

04 url路径传参

通过URL路径传递参数,一般用法/book/1:查询第一本书的数据

 main

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
)

var responseData = map[string]interface{}{
	"message": "success",
}

func main() {
	GinObj := gin.Default()
  GinObj.GET("/book/:name/:age/:bookId", get) // 可以是多个:名字,用/分割,所有参数都要传,否则404
	GinObj.POST("/book", post)
	GinObj.Run()
}

func get(c *gin.Context) {
	bookId := c.Param("bookId") // 获取url路径传的数据
	params := c.Params // 获取所有的url路径传的数据
	value, ok := params.Get("name") // value是数据,ok是是否有数据
	fmt.Println(value, ok)
	responseData["id"] = bookId
	c.JSON(200, responseData)
}

注意:

​ url路径传参,是在路径后边,且可以传多个,在定义的路径后边用:变量名格式接收,多个时,用/分割

​ 所有的参数必填,一般不要超过两个

4 文件上传

  • 01 单文件上传

  • 02 多文件上传

5 重定向

  • Redirect

内部外部都支持

重定向url既可以是完整的:https://www.baidu.com;也可以是内部的,且不以http开头的url都认为是内部的

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
)


func main() {
	GinObj := gin.Default()
	GinObj.GET("/book", getRedirect)
	GinObj.Run()
}

func getRedirect(gc *gin.Context) {
	gc.Redirect(302, "https://www.baidu.com") // 重定向到百度首页
}

注意:

gc.Redirect(): 第一个参数是数字状态码,第二个是全路径

6 路由

  • 自定义404时,页面
package main

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
)


func main() {
	GinObj := gin.Default()
	GinObj.NoRoute(noPage) // 没有匹配到路由时,调用该函数
	GinObj.Run()
}

func noPage(gc *gin.Context) {
	gc.JSON(200, "404页面")
}

01 路由组

方便管理,将拥有共同前缀的url划分为一个路由组。一般同一个组的路由,用{}包起来

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
)

var responseData = map[string]interface{}{
	"message": "success",
}

func main() {
	GinObj := gin.Default()
	GinObj.NoRoute(noPage)
	bookGroup := GinObj.Group("/book") // book路由组,都以/book为前缀
	{
		bookUserGroup := bookGroup.Group("/user") // 路由组嵌套,相当于/book/user
    bookUserGroup.GET("/:id", book) // 相当于路由是:/book/user/:id
	}
	GinObj.Run()
}
func book(gc *gin.Context) {
	id := gc.Param("id")
	responseData["id"] = id
	gc.JSON(200, responseData)
}

注意:

​ 路由组一般最多嵌套一层,建议路由组不嵌套

路由分文件(分app)管理

7 中间件

处理请求的过程中加入自定义的钩子函数,也就是中间件

01 定义中间件

中间件必须是gin.HandelFunc类型

func StaCost() gin.HandlerFunc { // StaCost是一个中间件,返回值必须是gin.HandlerFunc类型
	return MiddlewareFunc
}

func MiddlewareFunc(request *gin.Context) {
	start := time.Now()
	request.Set("message", "这是一个中间件") // 给初始的路由对象添加数据,值可以是任意类型
  data, ok := request.Get("message") // 从路由对象中获取数据,第一个数据,第二个是布尔值
	request.Next() // 继续往下运行
	//gc.Abort() // 不再往下运行
	cost := time.Since(start)
	fmt.Println(cost)
}

02 注册中间件

1 全局注册

所有的请求(url)都要经过中间件才能进入业务逻辑

package main 

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func main() {
  request := gin.New() // 实例化一个空的路由对象
	request.Use(StaCost())  // 全局注册使用该中间件
	request.Run()
  
}

func StaCost() gin.HandlerFunc {
	return middlewareFunc
}

func middlewareFunc(request *gin.Context) {
	start := time.Now()
	request.Set("message", "这是一个中间件")
	fmt.Println(request.Get("message"))

	request.Next()
	//gc.Abort()
	cost := time.Since(start)
	fmt.Println(cost)
}

注意:

gin.New()实例化一个新的没有任何中间件的路由对象

gin.Default()实例化的路由对象,包含两个中间件Logger Recovery

  • Logger:中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery:中间件会recover任何panic。如果有panic的话,会写入500响应码。
2 单个路由注册

只有请求该路由时,才进入该中间件,一个路由可以注册多个中间件

package main 

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func main() {
  request := gin.New() // 实例化一个空的路由对象
  request.GET("/id", m1(), m2(), ShopCart) //  只有请求该路由时该路由,可以注册多个中间件,执行顺序从左到右
	request.Run() 
}

func m1() gin.HandlerFunc {
	return MiddlewareFunc
}

func MiddlewareFunc(request *gin.Context) {
	request.Set("message", "这是一个中间件")
	fmt.Println(request.Get("message"))
	fmt.Println("我是第一个中间件")
	request.Next()
}

func m2() gin.HandlerFunc {
	return MiddlewareFunc1
}

func MiddlewareFunc1(request *gin.Context) {
	request.Set("message1", "这是第二个中间件")
	fmt.Println(request.Get("message1"))
	fmt.Println("我是第二个中间件")
	request.Next()
}
func ShopCart(request *gin.Context) {
	request.JSON(200, "shop ok")
}

注意:

​ 多个中间件时,把优先级高的放在左边

3 路由组注册

只要请求是该路由组,就进入该中间件,可以有多个中间件

第一种:

package main 

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func main() {
  request := gin.New() // 实例化一个空的路由对象
  group := request.Group("/shop", m1(), m2())  //  只要请求该路由组就进入该中间件,可以注册多个中间件,执行顺序从左到右
  {
  group.GET("/id", ShopCart) 
  }
	request.Run() 
}

func m1() gin.HandlerFunc {
	return MiddlewareFunc
}

func MiddlewareFunc(request *gin.Context) {
	request.Set("message", "这是一个中间件")
	fmt.Println(request.Get("message"))
	fmt.Println("我是第一个中间件")
	request.Next()
}

func m2() gin.HandlerFunc {
	return MiddlewareFunc1
}

func MiddlewareFunc1(request *gin.Context) {
	request.Set("message1", "这是第二个中间件")
	fmt.Println(request.Get("message1"))
	fmt.Println("我是第二个中间件")
	request.Next()
}
func ShopCart(request *gin.Context) {
	request.JSON(200, "shop ok")
}

第二种:

package main 

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func main() {
  request := gin.New() // 实例化一个空的路由对象
  group := request.Group("/shop")  
  group.Use(m1(), m2())  // 只要请求该路由组就进入该中间件,可以注册多个中间件,执行顺序从左到右
  {
  group.GET("/id", ShopCart) 
  }
	request.Run() 
}

func m1() gin.HandlerFunc {
	return MiddlewareFunc
}

func MiddlewareFunc(request *gin.Context) {
	request.Set("message", "这是一个中间件")
	fmt.Println(request.Get("message"))
	fmt.Println("我是第一个中间件")
	request.Next()
}

func m2() gin.HandlerFunc {
	return MiddlewareFunc1
}

func MiddlewareFunc1(request *gin.Context) {
	request.Set("message1", "这是第二个中间件")
	fmt.Println(request.Get("message1"))
	fmt.Println("我是第二个中间件")
	request.Next()
}
func ShopCart(request *gin.Context) {
	request.JSON(200, "shop ok")
}

一个中间件可以被多个路由或者路由组注册,同时一个路由或者路由组也可以注册多个中间件

03 其他

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

8 运行多个服务

多端口启动服务

posted @ 2021-03-11 11:50  tianzhh_lynn  阅读(313)  评论(0编辑  收藏  举报