Gin
环境:https://goproxy.cn,driect
github.com/gin-gonic/gin
介绍
Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,性能要好得多,多亏了 httprouter,速度提高了 40 倍。 如果您需要性能和良好的生产力,您一定会喜欢 Gin。
在本节中,我们将介绍 Gin 是什么,它解决了哪些问题,以及它如何帮助你的项目。
或者, 如果你已经准备在项目中使用 Gin,请访问快速入门.
源码分析
- type Handler interface {
- ServeHTTP(ResponseWriter, *Request)
- }
复制代码实现
- // ServeHTTP conforms to the http.Handler interface.
- func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- c := engine.pool.Get().(*Context)
- c.writermem.reset(w)
- c.Request = req
- c.reset()
- engine.handleHTTPRequest(c)
- engine.pool.Put(c)
- }
复制代码
- 通过对象池来减少内存申请和GC回收的消耗
- 取出来要用的时候再初始化
<img alt="gin" loading="lazy">
特性
快速
基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
支持中间件
传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
Crash 处理
Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!
JSON 验证
Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
路由组
更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
错误管理
Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
内置渲染
Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
可扩展性
新建一个中间件非常简单,去查看示例代码吧。
快速入门
- package main
- import "github.com/gin-gonic/gin"
- func main() {
- r := gin.Default()
- r.GET("/ping", func(c *gin.Context) {
- c.JSON(200, gin.H{
- "message": "pong",
- })
- })
- r.Run(":8000")// 监听并在 0.0.0.0:8080 上启动服务
- }
复制代码
- gin.Default()默认使用了Logger和Recover中间件
- Logger是负责进行打印输出日志的中间件,方便开发者进行程序的调试
- Recover如果程序执行过程中遇到了panic中断了服务,则Recover会恢复程序的运行并返回500的内部错误。
Engine
- type Engine struct {
- RouterGroup
- RedirectTrailingSlash bool
- RedirectFixedPath bool
- HandleMethodNotAllowed bool
- ForwardedByClientIP bool
- AppEngine bool
- UseRawPath bool
- UnescapePathValues bool
- RemoveExtraSlash bool
- RemoteIPHeaders []string
- TrustedPlatform string
- MaxMultipartMemory int64
- delims render.Delims
- secureJSONPrefix string
- HTMLRender render.HTMLRender
- FuncMap template.FuncMap
- allNoRoute HandlersChain
- allNoMethod HandlersChain
- noRoute HandlersChain
- noMethod HandlersChain
- pool sync.Pool
- trees methodTrees
- maxParams uint16
- maxSections uint16
- trustedProxies []string
- trustedCIDRs []*net.IPNet
- }
复制代码 请求处理
HTTP 协议的 8 种请求类型介绍
HTTP 协议中共定义了八种方法或者叫“动作”来表明对Request-URI指定的资源的不同操作方式,具体介绍如下:
- OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。
- HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
- GET:向特定的资源发出请求。
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
- PUT:向指定资源位置上传其最新内容。
- DELETE:请求服务器删除 Request-URI 所标识的资源。
- TRACE:回显服务器收到的请求,主要用于测试或诊断。
- CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
通用处理
Handle方法
- func (group *RouterGroup)Handle(httpMethod,relativePath string,handler...Handler)
复制代码
- httpMethod:表示要处理的HTTP请求类型,8种请求方式之一
- relativePath:表示要解析的接口,由开发者定义
- handlers:处理对应的请求的代码定义
Restful风格的API
- gin支持Restful风格的API
- 即Representational State Transfer的缩写。直接翻译的意思是”表现层状态转化”,是一种互联网应用程序的API设计理念:URL定位资源,用HTTP描述操作
1.获取文章 /blog/getXxx Get blog/Xxx
2.添加 /blog/addXxx POST blog/Xxx
3.修改 /blog/updateXxx PUT blog/Xxx
4.删除 /blog/delXxxx DELETE blog/Xxx
GET请求处理
路径参数- /:xx - Param("xx")
对于类似这样的请求:http://127.0.0.1:8080/index/12,那么如何获取最后路径中12的值呢?- package main
- import (
- "fmt"
- "github.com/gin-gonic/gin"
- "net/http"
- )
- func Index(ctx *gin.Context) {
- id := ctx.Param("id")
- fmt.Println(id)
- ctx.String(http.StatusOK, "success!")
- }
- func main() {
- router := gin.Default()
-
- // 路径参数获取,如:http://127.0.0.1:8080/index/12,获取12
- router.GET("/index/:id", Index)
- router.Run(":8080")
- }
复制代码 在挂载路由时需要通过":"来进行匹配,然后在视图函数中通过ctx.Param方法获取。
查询参数-/hello?xx=..? -Query()
对于类似这样的请求:http://127.0.0.1:8080/index1?id=12,那么如何获取最后路径中12的值呢?
1、ctx.Query
传参:http://127.0.0.1:8080/index1?id=12
路由:router.GET("/index1", Index1)
视图函数获取:ctx.Query("id")
2、ctx.DefaultQuery
传参:http://127.0.0.1:8080/index2
路由:router.GET("/index2", Index2)
视图函数获取:ctx.DefaultQuery("id", "0")
如果没有获取到id,就得到默认值0.
3、ctx.QueryArray
传参:http://127.0.0.1:8080/index3?id=1,2,3,4,5
路由:router.GET("/index3", Index3)
视图函数获取:ctx.QueryArray("id")
4、ctx.QueryMap
传参:http://127.0.0.1:8080/index4?user[name]="lily"&user[age]=15
路由:router.GET("/index4", Index4)
视图函数获取:ctx.QueryMap("user")
通用参数匹配 /user/:name/*action
当我们需要动态参数的路由时,如 /user/:id,通过调用不同的参数 :id 动态获取相应的用户信息。其中 /user/:id/*type,*type 的参数为可选。- package main
- import (
- "net/http"
- "strings"
- "github.com/gin-gonic/gin"
- )
- func main() {
- r := gin.Default()
- r.GET("/user/:name/*action", func(c *gin.Context) {
- name := c.Param("name")
- action := c.Param("action")
- //截取/
- action = strings.Trim(action, "/")
- c.String(http.StatusOK, name+" is "+action)
- })
- //默认为监听8080端口
- r.Run(":8000")
- }
复制代码 正常的结果:- http://localhost:8080/api/hjz/HJZ114152
- hjz is HJZ114152
复制代码 注释掉:Trim()后- http://localhost:8080/api/hjz/HJZ114152
- hjz is /HJZ114152
复制代码 POST请求处理
表单传输为post请求,http常见的传输格式为四种:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
普通方式提交表单-post -PostForm("xxx")
1、ctx.PostForm
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="/post_index" method="post">
- <p>用户名:<input type="text" name="username"></p>
- <p>密 码:<input type="password" name="password"></p>
- <p><input type="submit"></p>
- </form>
- </body>
- </html>
复制代码- ...
- func PostIndex(ctx *gin.Context) {
- username := ctx.PostForm("username")
- password := ctx.PostForm("password")
- fmt.Printf("用户名:%s, 密码:%s", username, password)
- ctx.String(http.StatusOK, "提交成功!")
- }
- ...
复制代码 2、ctx.PostFormMap
- Title<!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="/post_index" method="post">
- <p>用户名:<input type="text" name="username"></p>
- <p>密 码:<input type="password" name="password"></p>
- <p><input type="submit"></p>
- </form>
- </body>
- </html>
复制代码- ...
- func PostIndex1(ctx *gin.Context) {
- userMap := ctx.PostFormMap("user")
- fmt.Println(userMap)
- ctx.String(http.StatusOK, "提交成功!")
- }
- ...
复制代码 3、ctx.DefaultPostForm、ctx.PostFormArray
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="/post_index2" method="post">
- <p>用户名:<input type="text" name="username"></p>
- <p>密 码:<input type="password" name="password"></p>
- <p>爱 好:
- 读书<input type="checkbox" name="hobby" value="1">
- 看电影<input type="checkbox" name="hobby" value="2">
- 音乐<input type="checkbox" name="hobby" value="3">
- </p>
- <p><input type="submit"></p>
- </form>
- </body>
- </html>
复制代码- ...
- func PostIndex2(ctx *gin.Context) {
- username := ctx.PostForm("username")
- password := ctx.PostForm("password")
- age := ctx.DefaultPostForm("age", "0")
- hobby := ctx.PostFormArray("hobby")
- fmt.Printf("用户名:%s, 密码:%s, 年龄:%s, 爱好:%s", username, password, age, hobby)
- ctx.String(http.StatusOK, "提交成功!")
- }
- ...
复制代码例子
- package main
- import (
- "fmt"
- "github.com/gin-gonic/gin"
- "net/http"
- )
- func GetIndex(ctx *gin.Context) {
- ctx.HTML(http.StatusOK, "index.html", nil)
- }
- func PostIndex(ctx *gin.Context) {
- username := ctx.PostForm("username")
- password := ctx.PostForm("password")
- fmt.Printf("用户名:%s, 密码:%s", username, password)
- ctx.String(http.StatusOK, "提交成功!")
- }
- func GetIndex1(ctx *gin.Context) {
- ctx.HTML(http.StatusOK, "index1.html", nil)
- }
- func PostIndex1(ctx *gin.Context) {
- userMap := ctx.PostFormMap("user")
- fmt.Println(userMap)
- ctx.String(http.StatusOK, "提交成功!")
- }
- func GetIndex2(ctx *gin.Context) {
- ctx.HTML(http.StatusOK, "index2.html", nil)
- }
- func PostIndex2(ctx *gin.Context) {
- username := ctx.PostForm("username")
- password := ctx.PostForm("password")
- age := ctx.DefaultPostForm("age", "0")
- hobby := ctx.PostFormArray("hobby")
- fmt.Printf("用户名:%s, 密码:%s, 年龄:%s, 爱好:%s", username, password, age, hobby)
- ctx.String(http.StatusOK, "提交成功!")
- }
- func main() {
- router := gin.Default()
- router.LoadHTMLGlob("template/*")
- // ctx.PostForm
- router.GET("/get_index", GetIndex)
- router.POST("/post_index", PostIndex)
- // ctx.PostFormMap
- router.GET("/get_index1", GetIndex1)
- router.POST("/post_index1", PostIndex1)
- // ctx.DefaultPostForm、ctx.PostFormArray
- router.GET("/get_index2", GetIndex2)
- router.POST("/post_index2", PostIndex2)
- router.Run(":8080")
- }
复制代码 Ajax方式提交表单
ajax的后台处理逻辑与普通的表单的提交的处理方式基本相同,只不过在返回的时候需要返回json数据,前台使用回调函数进行处理。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
-
- </head>
- <body>
- <form>
- <p>用户名:<input id="username" type="text"></p>
- <p>密码:<input id="password" type="password"></p>
- <p><input type="button" value="提交" id="btn_submit"></p>
- </form>
- </body>
- </html>
复制代码- package main
- import (
- "fmt"
- "github.com/gin-gonic/gin"
- "net/http"
- )
- func GetIndex(ctx *gin.Context) {
- ctx.HTML(http.StatusOK, "index.html", nil)
- }
- func PostIndex(ctx *gin.Context) {
- username := ctx.PostForm("username")
- password := ctx.PostForm("password")
- fmt.Println(username, password)
- data := map[string]interface{}{
- "code": 2000,
- "message": "成功",
- }
- ctx.JSON(http.StatusOK, data)
- }
- func main() {
- router := gin.Default()
- router.LoadHTMLGlob("template/*")
- router.Static("/static", "static")
- router.GET("/get_index", GetIndex)
- router.POST("/post_index", PostIndex)
- router.Run(":8080")
- }
复制代码 参数绑定
无论时get请求的参数还是post请求的请求体,在后台都需要通过对应的方法来获取对应参数的值,那么有没有一种方式能够让我们定义好请求数据的格式,然后自动进行获取,这里可以通过参数绑定的方式来进行处理。它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
这里以get请求的查询参数为例:
- http://127.0.0.1:8080/index?username=%22llkk%22&password=%22123%22
复制代码- package main
- import (
- "fmt"
- "github.com/gin-gonic/gin"
- "net/http"
- )
- type User struct {
- Username string `form:"username" json:"username"`
- Password string `form:"password" json:"password"`
- }
- func Index(ctx *gin.Context) {
- var user User
- err := ctx.ShouldBind(&user)
- fmt.Println(err)
- fmt.Println(user.Username, user.Password) // "llkk" "123"
- ctx.String(http.StatusOK, "success")
- }
- func main() {
- router := gin.Default()
- router.GET("/index", Index)
- router.Run(":8080")
- }
复制代码 文件处理
上传单个文件
- multipart/form-data格式用于文件上传
- gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Document</title>
- </head>
- <body>
- <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
- 上传文件:<input type="file" name="file" >
- <input type="submit" value="提交">
- </form>
- </body>
- </html>
复制代码 [code]package mainimport ( "github.com/gin-gonic/gin")func main() { r := gin.Default() //限制上传最大尺寸 r.MaxMultipartMemory = 8 |