
- 座 驾
- 黑色保时捷356A(德国生产的古董车)
- 真实姓名
- 黑泽阵
- 饰 演
- 佐佐木藏之介
- 搭 档
- 伏特加
- 配 音
- 日语原版:堀之纪 、、大陆版:王明军、文涛、冯盛
- 身 份
- 黑衣组织成员
- 别 名
- 杜松子酒、金恩、阿进等
- 中文名
- 琴酒
- 性 别
- 男
- 外文名
- Gin ジン(Jin)
- 年 龄
- 不详
- 惯用手
- 左手
- 登场作品
- 《名侦探柯南》及其衍生系列
-
Gin
2021-04-02 14:39:38Gin闪亮登场 简介 Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。 Gin特点主要有: 速度快、性能优;支持中间件操作,方便编码处理;非常简单的实现路由等。 安装gin框架库 go get -u github....Gin闪亮登场
简介
Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。
Gin特点主要有: 速度快、性能优;支持中间件操作,方便编码处理;非常简单的实现路由等。安装gin框架库
go get -u github.com/gin-gonic/gin
基架搭建
package main import ( "github.com/gin-gonic/gin" ) func main() { // 创建一个 默认的 路由引擎 r := gin.Default() // 路由 和 控制器 r.GET("/hello", func(c *gin.Context) { c.JSON(200, gin.H{ "hello": "world", }) }) // 服务监听 err := r.Run("localhost:8080") // 默认 监听并在 0.0.0.0:8080 上启动服务 if err != nil { panic(err) } }
路由引擎
在gin框架中,
路由引擎
是一个 一个结构体,其中包含了路由组、中间件、页面渲染接口、等相关内容。engine1 = gin.Default() // 创建默认的 路由引擎 engine2 = gin.New() // 创建一个新的 路由引擎
- 说明:
gin.Default其实也使用gin.New()创建engine实例,但是会默认使用Logger
和Recovery
中间件。 - Logger是负责进行打印并输出日志的中间件,方便开发者进行程序调试;
- Recovery中间件的作用是如果程序执行过程中遇到panic中断了服务,则Recovery会恢复程序执行,并返回服务器500内部错误。
- 通常情况下,我们使用默认的gin.Default创建Engine实例。
1. 项目配置
这是一种 通用的 项目配置文件 的手段。
1. 配置文件: config/app.json
{ "app_name": "cloudrestaurant", "app_mode": "debug", "app_host": "localhost", "app_port": "8090", "database": { "dbsize": "mysql", "username": "root", "password": "123456", "host": "localhost", "port": "3679", "dbname": "gin", "charset": "utf8mb4" }, "redis_config": { "addr": "127.0.0.1", "port": "6379", "password": "", "db": 0 } }
2. 解析文件: app/app.go
package app import ( "bufio" "encoding/json" "os" ) type Config struct { AppName string `json:"app_name"` AppMode string `json:"app_mode"` AppHost string `json:"app_host"` AppPort string `json:"app_port"` Database Database `json:"database"` RedisConfig RedisConfig `json:"redis_config"` } type Database struct { DBsize string `json:"dbsize"` Username string `json:"username"` Password string `json:"password"` Host string `json:"host"` Port string `json:"port"` DBname string `json:"dbname"` Charset string `json:"charset"` } type RedisConfig struct { Addr string `json:"addr"` Port string `json:"port"` Password string `json:"password"` Db int `json:"db"` } var AppConfig *Config = nil func ParseConfig(path string) { file, err := os.Open(path) if err != nil { panic(err) } defer file.Close() reader := bufio.NewReader(file) decoder := json.NewDecoder(reader) err = decoder.Decode(&AppConfig) if err != nil { panic(err) } } func init() { ParseConfig("./config/app.json") }
强调:
在Gin中,项目开发完最终所有的包 都会打包成一个可执行文件,这个文件和main包同目录,所以 项目中凡是设计
相对路径
的 都要站在main包的角度
去写相对路径。3. 激活使用
package main import ( // 在main文件引入,即 激活配置 "项目名称/app" "fmt" ) func main() { // 使用配置中的数据 fmt.Println(app.AppConfig.AppName) fmt.Println(app.AppConfig.Database.DBname) }
2. 控制器
外观描述
是一个函数, 只有一个参数, 参数类型为:*gin.Context, 没有返回值,就是这么简单。
*gin.Context 参数是一个结构体, 是对 请求、响应报文 的封装,是Gin框架的主要内容,是上下文
!!!上下文(gin.Context) 上 的 api
- Next()
将处理权 移交到 下一棒 中间件(或控制器) 手中
- Abort()
终断该请求的处理
- Set(“name”, “tom”)、Get(“name”)
在上下文中 写入数据,可以在 下一棒 中间件(或控制器) 手中 通过 Get()获取到对应数据
- 重定向
Redirect(302, “https://www.baidu.com”)
- 请求报文: Request
- c.Request.Proto => 请求协议
- c.Request.Host => 请求协议
- c.Request.URL => 请求路径
- c.Request.Method => 请求方法
- c.Request.Header[“Content-Type”] => 请求头相关
控制器 的实现
package controller import ( "github.com/gin-gonic/gin" ) // 将 控制器绑在 结构体上 是 模块化 思想的体现 // 很好的 避免了 不同路由 的 控制器 重名的问题。 type Testing struct { } func (that *Testing) HandGetTest(context *gin.Context) { ... } func (that *Testing) HandPostTest(context *gin.Context) { ... }
3. 路由
单路由
使用
路由引擎
上封装 路由相关的api 可以很方便地实现 路由功能package router import ( "testGin/controller" "github.com/gin-gonic/gin" ) // 路由表 func ActiveRouterList(e *gin.Engine) { e.Handle("GET", "/test", new(controller.Testing).HandGetTest) // "语法糖" e.GET("/test", new(controller.Testing).HandGetTest) e.POST("/test", new(controller.Testing).HandPostTest) }
- 在 main函数 中 激活🔥路由表
package main import ( "testGin/router" "github.com/gin-gonic/gin" ) func main() { app := gin.Default() // 激活🔥路由表 router.ActiveRouterList(app) app.Run() }
路由组
使用 路由引擎 的 Group 方法
// 激活 路由表 func ActiveRouterList(e *gin.Engine) { group := e.Group("/aaa") { // 加这个 块级括号 ,是为了 彰显 路由组 层次感 group.GET("/b1", new(controller.Testing).HandGetTest) group.GET("/b2", new(controller.Testing).HandGetTest) group2 := group.Group("b3") { group2.GET("/c1", new(controller.Testing).HandGetTest) group2.GET("/c2", new(controller.Testing).HandGetTest) } } // 路由组访问地址: /aaa/b1 和 /aaa/b3/c1 }
其他路由
404路由
e.NoRoute(控制器)
广角路由
// 可以匹配 任何请求方法 e.Any("test", 控制器)
静态路由
上面的路由都是动态路由,下面介绍一下 静态路由。
静态路由,为静态文件 指定 的访问路径。工程目录下 的
static文件夹
下的images文件夹
下存放这aaa.png
图片文件,
写一个静态路由来访问这个文件。// 路由表 func ActiveRouterList(e *gin.Engine) { e.Static("/img", "./static/images/") // 图片的访问路径: /img/aaa.png }
4. 中间件
gin框架允许开发者在 处理请求 的过程中,加入用户自己的 钩子函数。这个 钩子函数 就是 中间件。
中间件适合处理一些 公共的 业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
Gin 的中间件 是 洋葱模型的 运行流程。定义一个中间件
// 设计一个 统计 请求耗时 的中间件(默认路由引擎 中已经实现,这里重点定义中间件) package midlleware import ( "fmt" "time" "github.com/gin-gonic/gin" ) // 中间件 的 本质上 就是 控制器 生产函数 // 所以 返回值 是一个 控制器 func TimeCost() (gin.HandlerFunc) { return func(c *gin.Context) { start := time.Now() c.Next() // 将控制权 主动交给 下一棒 cost := time.Since(start) fmt.Println(cost) } }
注册 使用 中间件
单路由 注册
// 在路由表中,找到 对应的路由 加入即可 e.GET("/test", midlleware.TimeCost(), new(controller.Testing).HandGetTest) // 注意,要以调用的方式 放入 中间件
全局 注册
// 在路由表中 e.Use(midlleware.TimeCost())
路由组 注册
// 方式一, 单路由注册 的方式 group := e.Group("aaa", midlleware.TimeCost()) { ... } // 方式二, 全局 注册 的方式 group := e.Group("aaa") group.Use(midlleware.TimeCost()) { ... }
5. 前后交互
接收数据
api直取
获取get 请求的 Param参数(url参数)
e.GET("/test/:name", func(c *gin.Context){ name := c.Param("name") })
获取get 的 请求参数
e.GET("/test", func(c *gin.Context){ name := c.Query("name") })
说明:
如果 请求参数 name是必须的,但是客户访问的时候又不一定传时,我们可以为 name设置一个
默认值:
name := c. DefaulQuery("name", " 默认值 ")
获取 POST 以
表单的形式
提交数据engine.Handle("POST", "/login", func(c *gin.Context) { username := c.PostForm("username") password := c.PostForm("pwd") })
- 说明:
post请求参数 也可以设置默认值:
name := c. DefaultPostForm ("name ", "默认值")
对应绑定
api直取 的 方式 获取数据 有两方面的 缺点:
- 无法获取 POST 以
json
形式 提交的数据 - 一个一个获取单个属性键值对,如果要获取的键值对多了,再一个个获取,就有无语了。
获取 GET 的 请求参数 (ShouldBindQuery)
/aaa?name=tom&age=10
通过 ShouldBindQuery()方法实现package router import ( "fmt" "github.com/gin-gonic/gin" ) //form之后的名 和 提交 数据 的键名 对应不起来, //则无法做到对应赋值 type man struct { Name string `form:"name"` Age int `form:"age"` } // 路由表 func ActiveRouterList(e *gin.Engine) { e.GET("/aaa", func(c *gin.Context) { var peop man err := c.ShouldBindQuery(&peop) if err != nil { fmt.Println(err) } fmt.Printf("%T >>---> %+v\n", peop, peop) //router.man >>---> {Name:tom Age:10} c.JSON(200, gin.H{"hi": "hi"}) }) }
获取 POST 以
json形式
提交数据 (ShouldBindJSON)通过
ShouldBindJSON()
方法实现
只需将上面的 ShouldBindQuery 改为 BindJSON 即可,其他都不用变。获取 POST 以
表单的形式
提交数据 (ShouldBind)通过
ShouldBind()
方法实现
只需将上面的 ShouldBindQuery 改为 ShouldBind 即可,其他都不用变。获取上传文件
单文件
e.POST("/aaa", func(c *gin.Context) { file, err := c.FormFile("avatar") if err != nil { fmt.Println(err) } // SaveUploadedFile 不会创建文件夹, // 所以 确保 filePath 上的文件夹都是已经存在的。 // 这里最好 用一下uuid var filePath = "./uploadfile/" + file.Filename err = ctx.SaveUploadedFile(file, filePath) }
多文件
e.POST("/aaa", func(c *gin.Context) { form, _ := c.MultipartForm() files := form.File["file"] for index, file := range files { var filePath = "./uploadfile/" + file.Filename // 上传文件到指定的目录 c.SaveUploadedFile(file, filePath) } }
响应数据
返回普通 字符串
涉及到的 api是 gin.Context 上的
Writer
实例[]byte
engine.GET("/hello", func (c *gin.Context) { var s []byte = []byte{'a', 'b', 88} c.Writer.Write(s) // 前端收到的是: abX })
string
var s string = "abc" c.Writer.WriteString(s) // 前端收到的是: abc
返回 json 字符串
涉及到的 api是 gin.Context 上的
JOSN
方法map
e.POST("/aaa", func(c *gin.Context) { m := map[string]interface{}{ "name": "tom", "age": 11, } c.JSON(200, m) // 返回给前端的数据: `{ "age": 11,"name": "tom"}` })
struct
e.POST("/aaa", func(c *gin.Context) { obj := struct { Name string `json:"name"` Age int `json:"old"` }{"tom", 8} c.JSON(200, obj) // 返回给前端的数据: `{"name": "tom", "old": 8}` })
6. 数据模型
请转到我的GORM那一篇。
7. 会话管理
获取cookie
e.GET("/get", func(c *gin.Context) { cookie, _ := c.Cookie("cookie_key") fmt.Println(cookie) })
建议: token + cookie
8. 日志记录
通过使用logrus日志框架,以中间件的方式 实现日志记录
package midlleware import ( "fmt" "os" "path" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) // 判断目录是否存在 func isExist(filePath string) (bool, error, string) { _, err := os.Stat(filePath) if err == nil { return true, nil, "“文件存在”" } else { if os.IsNotExist(err) { return false, nil, "“文件不存在”" } else { return false, err, "“包错原因不是因为文件不存在导致的”" } } } //日志记录到文件 func LoggerToFile() gin.HandlerFunc { logFilePath := "./logFile" logFileName := "logName" //文件路径 fileName := path.Join(logFilePath, logFileName) res, _, _ := isExist(fileName) if !res { err := os.Mkdir("./logFile", os.ModePerm) if err != nil { fmt.Println(err) } } // 写入文件 file, err := os.OpenFile(fileName, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0700) if err != nil { fmt.Println("err", err) } logger := logrus.New() // 设置输出 logger.Out = file // 设置日志级别 logger.SetLevel(logrus.DebugLevel) return func(c *gin.Context) { startTime := time.Now() c.Next() endTime := time.Now() // 下面是 要写入日志的 字段 // 执行时间 allTime := endTime.Sub(startTime) // 请求方式 reqMethod := c.Request.Method // 请求路由 reqUrl := c.Request.RequestURI // 状态码 statusCode := c.Writer.Status() // 请求ip clientIP := c.ClientIP() // 换一下日期格式 logger.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "2006-01-02 15:04:05", }) // 换成json格式 logger.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", }) // 日志格式一: 日志变成json entry := logger.WithFields(logrus.Fields{ "status_code": statusCode, "all_time": allTime, "client_ip": clientIP, "req_method": reqMethod, "req_url": reqUrl, }) entry.Info() // //日志格式二: 日志是字符串 // logger.Infof("| %3d | %13v | %15s | %s | %s |", // statusCode, // allTime, // clientIP, // reqMethod, // reqUrl, // ) } }
- 注意:
日志的文件存储路径 通过配置文件来决定,这里只是简单的演示。
9. 错误处理
404 路由 + 重定向 or 制定404控制器
10. 安全防护
- 说明:
-
gin
2020-05-06 18:56:22安装gin框架时出现链接超时的问题 描述 安装gin 框架时:go get github.com/gin-gonic/gin出现unrecognized import path "google.golang.org/protobuf/encoding/prototext": ...安装gin框架时出现链接超时的问题
描述
安装gin 框架时:go get github.com/gin-gonic/gin出现unrecognized import path "google.golang.org/protobuf/encoding/prototext": https fetch: Get "https://google.golang.org/protobuf/encoding/prototext?go-get=1": dial tcp 216.239.37.1:443: i/o timeout等问题
解决方案:
由于无法翻墙,我们可以设置通过设置代理来实现https://goproxy.cn/ 如下,在命令行输入: $ go env -w GO111MODULE=on $ go env -w GOPROXY=https://goproxy.cn,direct 然后执行go get就可以快速下载了
-
Gin Web Framework Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance ...
-
gin.v1 instead of gin
2020-12-04 11:44:51<div><p>In this new update you change the gin framework version "github.com/gin-gonic/gin" instead of "gopkg.in/gin-gonic/gin.v1" <p>So I'm getting this error: cannot use cors.... -
gin框架安装以及gin的请求路由
2021-01-20 13:29:50window 上安装gin,需要先安装git ,go 环境,然后打开git,在你想要的地方安装go就可以 比如在 /e/golang/gin 安装gin 在git命令行中输入mkdir -p $GOPATH/gin1 然后没有报错的话就说明创建成功了,然后进入该文件下... -
Gin 创建项目脚本 gin
2020-10-07 20:39:19golang web框架——gin 脚本名称 : gin #!/bin/bash mkdir $GOPATH/src/$1 && cd $_ govendor init govendor fetch github.com/gin-gonic/gin vendor目录下就是本项目所需要的依赖。脚本名称 : gin
#!/bin/bash mkdir $GOPATH/src/$1 && cd $_ govendor init govendor fetch github.com/gin-gonic/gin
vendor目录下就是本项目所需要的依赖。
-
Gin support
2020-11-30 09:08:52<p>Implements a <code>wrapper/gin</code> subpackage as drop-in replacement for <a href="https://gin-gonic.github.io/gin">Gin Gonic</a> users. <p>Example and simple tests are included.</p><p>该提问来源... -
Bump github.com/gin-gonic/gin from 1.4.0 to 1.5.0
2020-12-06 12:18:56<div><p>Bumps <a href="https://github.com/gin-gonic/gin">github.com/gin-gonic/gin</a> from 1.4.0 to 1.5.0. Release notes *Sourced from [github.com/gin-gonic/gin's releases]... -
Gin Web框架简介
2016-04-14 18:01:07翻译自: https://github.com/gin-gonic/gin/blob/develop/README.mdGin Web框架 Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务. 如果你希望...翻译自: https://github.com/gin-gonic/gin/blob/develop/README.md
Gin Web框架
Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务. 如果你希望构建一个高性能的生产环境,你会喜欢上使用 Gin。
$ cat test.go
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() // listen and server on 0.0.0.0:8080 }
基准测试
Gin基于HttpRouter的这个定制版本来构建。
Benchmark name (1) (2) (3) (4) BenchmarkAce_GithubAll 10000 109482 13792 167 BenchmarkBear_GithubAll 10000 287490 79952 943 BenchmarkBeego_GithubAll 3000 562184 146272 2092 BenchmarkBone_GithubAll 500 2578716 648016 8119 BenchmarkDenco_GithubAll 20000 94955 20224 167 BenchmarkEcho_GithubAll 30000 58705 0 0 BenchmarkGin_GithubAll 30000 50991 0 0 BenchmarkGocraftWeb_GithubAll 5000 449648 133280 1889 BenchmarkGoji_GithubAll 2000 689748 56113 334 BenchmarkGoJsonRest_GithubAll 5000 537769 135995 2940 BenchmarkGoRestful_GithubAll 100 18410628 797236 7725 BenchmarkGorillaMux_GithubAll 200 8036360 153137 1791 BenchmarkHttpRouter_GithubAll 20000 63506 13792 167 BenchmarkHttpTreeMux_GithubAll 10000 165927 56112 334 BenchmarkKocha_GithubAll 10000 171362 23304 843 BenchmarkMacaron_GithubAll 2000 817008 224960 2315 BenchmarkMartini_GithubAll 100 12609209 237952 2686 BenchmarkPat_GithubAll 300 4830398 1504101 32222 BenchmarkPossum_GithubAll 10000 301716 97440 812 BenchmarkR2router_GithubAll 10000 270691 77328 1182 BenchmarkRevel_GithubAll 1000 1491919 345553 5918 BenchmarkRivet_GithubAll 10000 283860 84272 1079 BenchmarkTango_GithubAll 5000 473821 87078 2470 BenchmarkTigerTonic_GithubAll 2000 1120131 241088 6052 BenchmarkTraffic_GithubAll 200 8708979 2664762 22390 BenchmarkVulcan_GithubAll 5000 353392 19894 609 BenchmarkZeus_GithubAll 2000 944234 300688 2648 (1): 总重复次数
(2): 单次请求耗时 (ns/op)
(3): 堆内存大小 (B/op)
(4): 单次请求内存分配数 (allocs/op)##Gin v1. stable
- 零分配路由.
- 从路由到写请求, 依然为最快的路由器和框架.
- 完备的单元测试套件.
- Battle tested.(?)
- API冻结, 新的release版不会影响现有的代码.
快速开始
-
下载并安装Gin:
$ go get github.com/gin-gonic/gin
-
在代码中import进来:
import "github.com/gin-gonic/gin"
-
(可选) Import
net/http
. 如果用到诸如http.StatusOK
的常量, 需要引入该包:import "net/http"
##API示例
使用 GET, POST, PUT, PATCH, DELETE 以及 OPTIONS
func main() { // Creates a gin router with default middleware: // logger and recovery (crash-free) middleware router := gin.Default() router.GET("/someGet", getting) router.POST("/somePost", posting) router.PUT("/somePut", putting) router.DELETE("/someDelete", deleting) router.PATCH("/somePatch", patching) router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options) // By default it serves on :8080 unless a // PORT environment variable was defined. router.Run() // router.Run(":3000") for a hard coded port }
路径参数
func main() { router := gin.Default() // This handler will match /user/john but will not match neither /user/ or /user router.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) // However, this one will match /user/john/ and also /user/john/send // If no other routers match /user/john, it will redirect to /user/john/ router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") message := name + " is " + action c.String(http.StatusOK, message) }) router.Run(":8080") }
查询字符串参数
func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") c.String(http.StatusOK, "Hello %s %s", firstname, lastname) }) router.Run(":8080") }
Multipart/Urlencoded表单提交
func main() { router := gin.Default() router.POST("/form_post", func(c *gin.Context) { message := c.PostForm("message") nick := c.DefaultPostForm("nick", "anonymous") c.JSON(200, gin.H{ "status": "posted", "message": message, "nick": nick, }) }) router.Run(":8080") }
更多示例: 查询参数 + POST表单提交
POST /post?id=1234&page=1 HTTP/1.1 Content-Type: application/x-www-form-urlencoded name=manu&message=this_is_great
func main() { router := gin.Default() router.POST("/post", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080") }
id: 1234; page: 1; name: manu; message: this_is_great
更多示例: 上传文件
参考问题 #548.
func main() { router := gin.Default() router.POST("/upload", func(c *gin.Context) { file, header , err := c.Request.FormFile("upload") filename := header.Filename fmt.Println(header.Filename) out, err := os.Create("./tmp/"+filename+".png") if err != nil { log.Fatal(err) } defer out.Close() _, err = io.Copy(out, file) if err != nil { log.Fatal(err) } }) router.Run(":8080") }
分组路由
func main() { router := gin.Default() // Simple group: v1 v1 := router.Group("/v1") { v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) v1.POST("/read", readEndpoint) } // Simple group: v2 v2 := router.Group("/v2") { v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) v2.POST("/read", readEndpoint) } router.Run(":8080") }
不使用中间件, 使用Gin默认配置
使用
r := gin.New()
来代替
r := gin.Default()
使用中间件
func main() { // Creates a router without any middleware by default r := gin.New() // Global middleware r.Use(gin.Logger()) r.Use(gin.Recovery()) // Per route middleware, you can add as many as you desire. r.GET("/benchmark", MyBenchLogger(), benchEndpoint) // Authorization group // authorized := r.Group("/", AuthRequired()) // exactly the same as: authorized := r.Group("/") // per group middleware! in this case we use the custom created // AuthRequired() middleware just in the "authorized" group. authorized.Use(AuthRequired()) { authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint) authorized.POST("/read", readEndpoint) // nested group testing := authorized.Group("testing") testing.GET("/analytics", analyticsEndpoint) } // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
model binding与验证
要绑定一个请求body到某个类型, 可以使用model binding。 目前支持JSON, XML 以及标准from格式 (foo=bar&boo=baz)的绑定。
所有你想要绑定的域(field), 需要你设置对应的绑定标识。 例如, 要绑定到JSON, 则这样声明
json:"fieldname"
。使用Bind方法时, Gin会尝试通过Content-Type头部来推定绑定的类型(如json还是form)。而如果你明确知道要绑定的类型, 可以使用BindWith方法。
你也可以指定哪些filed需要绑定。 如果某个filed像这样声明:
binding:"required"
, 那么在进行绑定时如果发现是空值(注: 是请求中不存在该field名?), 当前的请求会失败并收到错误提示。// Binding from JSON type Login struct { User string `form:"user" json:"user" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } func main() { router := gin.Default() // Example for binding JSON ({"user": "manu", "password": "123"}) router.POST("/loginJSON", func(c *gin.Context) { var json Login if c.BindJSON(&json) == nil { if json.User == "manu" && json.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } } }) // Example for binding a HTML form (user=manu&password=123) router.POST("/loginForm", func(c *gin.Context) { var form Login // This will infer what binder to use depending on the content-type header. if c.Bind(&form) == nil { if form.User == "manu" && form.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } } }) // Listen and server on 0.0.0.0:8080 router.Run(":8080") }
###Multipart/Urlencoded表单请求方式的绑定
package main import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" ) type LoginForm struct { User string `form:"user" binding:"required"` Password string `form:"password" binding:"required"` } func main() { router := gin.Default() router.POST("/login", func(c *gin.Context) { // you can bind multipart form with explicit binding declaration: // c.BindWith(&form, binding.Form) // or you can simply use autobinding with Bind method: var form LoginForm // in this case proper binding will be automatically selected if c.Bind(&form) == nil { if form.User == "user" && form.Password == "password" { c.JSON(200, gin.H{"status": "you are logged in"}) } else { c.JSON(401, gin.H{"status": "unauthorized"}) } } }) router.Run(":8080") }
使用以下命令测试:
$ curl -v --form user=user --form password=password http://localhost:8080/login
XML和JSON的渲染
func main() { r := gin.Default() // gin.H is a shortcut for map[string]interface{} r.GET("/someJSON", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) }) r.GET("/moreJSON", func(c *gin.Context) { // You also can use a struct var msg struct { Name string `json:"user"` Message string Number int } msg.Name = "Lena" msg.Message = "hey" msg.Number = 123 // Note that msg.Name becomes "user" in the JSON // Will output : {"user": "Lena", "Message": "hey", "Number": 123} c.JSON(http.StatusOK, msg) }) r.GET("/someXML", func(c *gin.Context) { c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) }) // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
####处理静态文件的请求
func main() { router := gin.Default() router.Static("/assets", "./assets") router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFile("/favicon.ico", "./resources/favicon.ico") // Listen and server on 0.0.0.0:8080 router.Run(":8080") }
####HTML模板渲染
Using LoadHTMLTemplates()
func main() { router := gin.Default() router.LoadHTMLGlob("templates/*") //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") router.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "Main website", }) }) router.Run(":8080") }
templates/index.tmpl
<html> <h1> {{ .title }} </h1> </html>
使用不同路径下但相同文件名的模板
func main() { router := gin.Default() router.LoadHTMLGlob("templates/**/*") router.GET("/posts/index", func(c *gin.Context) { c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ "title": "Posts", }) }) router.GET("/users/index", func(c *gin.Context) { c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ "title": "Users", }) }) router.Run(":8080") }
templates/posts/index.tmpl
{{ define "posts/index.tmpl" }} <html><h1> {{ .title }} </h1> <p>Using posts/index.tmpl</p> </html> {{ end }}
templates/users/index.tmpl
{{ define "users/index.tmpl" }} <html><h1> {{ .title }} </h1> <p>Using users/index.tmpl</p> </html> {{ end }}
你也可以使用自己的渲染模板
import "html/template" func main() { router := gin.Default() html := template.Must(template.ParseFiles("file1", "file2")) router.SetHTMLTemplate(html) router.Run(":8080") }
重定向
实现HTTP重定向并不麻烦:
r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") })
内部或外部的地址都是支持的。
定制中间件
func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // Set example variable c.Set("example", "12345") // before request c.Next() // after request latency := time.Since(t) log.Print(latency) // access the status we are sending status := c.Writer.Status() log.Println(status) } } func main() { r := gin.New() r.Use(Logger()) r.GET("/test", func(c *gin.Context) { example := c.MustGet("example").(string) // it would print: "12345" log.Println(example) }) // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
使用 BasicAuth() 中间件
// simulate some private data var secrets = gin.H{ "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, "austin": gin.H{"email": "austin@example.com", "phone": "666"}, "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, } func main() { r := gin.Default() // Group using gin.BasicAuth() middleware // gin.Accounts is a shortcut for map[string]string authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ "foo": "bar", "austin": "1234", "lena": "hello2", "manu": "4321", })) // /admin/secrets endpoint // hit "localhost:8080/admin/secrets authorized.GET("/secrets", func(c *gin.Context) { // get user, it was set by the BasicAuth middleware user := c.MustGet(gin.AuthUserKey).(string) if secret, ok := secrets[user]; ok { c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) } else { c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) } }) // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
中间件中的Goroutines
在一个middleware或handler中使用goroutine时, 你不能直接使用源gin.Context, 而只能使用它的一份只读拷贝。
func main() { r := gin.Default() r.GET("/long_async", func(c *gin.Context) { // create copy to be used inside the goroutine cCp := c.Copy() go func() { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // note that you are using the copied context "cCp", IMPORTANT log.Println("Done! in path " + cCp.Request.URL.Path) }() }) r.GET("/long_sync", func(c *gin.Context) { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // since we are NOT using a goroutine, we do not have to copy the context log.Println("Done! in path " + c.Request.URL.Path) }) // Listen and server on 0.0.0.0:8080 r.Run(":8080") }
自定义HTTP配置
直接使用
http.ListenAndServe()
, 示例:func main() { router := gin.Default() http.ListenAndServe(":8080", router) }
或者
func main() { router := gin.Default() s := &http.Server{ Addr: ":8080", Handler: router, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } s.ListenAndServe() }
平滑重启或关闭
是否需要平滑重启或关闭你的服务?有以下这些方式可以实现。
我们可以用 fvbock/endless来替换默认的方法
ListenAndServe
,详情请参考#296。router := gin.Default() router.GET("/", handler) // [...] endless.ListenAndServe(":4242", router)
或者, 除使用endless之外的方法:
- manners: 一种平滑关闭自己的Go HTTP服务。
-
Gin middleware
2020-12-27 10:12:50<div><p>I would very much like to see some middleware for this that works with the <a href="https://github.com/gin-gonic/gin">gin</a> framework.</p><p>该提问来源于开源项目:auth0/go-jwt-... -
开源项目-gin-gonic-gin.zip
2019-10-28 18:04:44开源项目-gin-gonic-gin.zip,What's this line in the Gin source code doing -
gin version
2020-12-09 00:25:51<p>Would it be ok to reuse/tweak the code here to produce a separate gin version? <p>The gin middleware specific part would look as follows: <pre><code> // Middleware is a gin native version of the ...