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)
}
注意:
路由组一般最多嵌套一层,建议路由组不嵌套
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 运行多个服务
多端口启动服务