Golang框架之gin
gin是目前golang的主要web框架之一,之所以选择这个框架是因为其拥有高效的路由性能,并且有人长期维护,目前github上的star数已经破3W。
[安装]
go get -u github.com/gin-gonic/gin
基础使用:
package main import (
"github.com/gin-gonic/gin"
"net/http"
) func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"messag": "pong",
})
}) r.Run(":8099")
}
运行服务: go run main.go
其中默认使用中间件: logger 和recovery
路由:
请求方法包括:get, post, patch, delete and options。此外还有any,即任何请求方法都会监听到。
func main() {
router := gin.Default()
router.GET("/someGet", handle)
router.POST("/somePost", handle)
router.PUT("/somePut", handle)
router.DELETE("/someDelete", handle)
router.PATCH("/somePatch", handle)
router.HEAD("/someHead", handle)
router.OPTIONS("/someOptions", handle)
router.ANY("/any", handle)
router.Run()
}
func handle(context *gin.Context) {
context.String(http.StatusOK, "hello world")
}
分组路由可以通过router.Group:
func main() {
router := gin.Default()
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
请求:
Path参数:
func main() {
router := gin.Default()
// 匹配/user/john
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 匹配/user/john/和/user/john/send
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")
}
query参数:
func main() {
router := gin.Default()
// welcome?firstname=Jane&lastname=Doe
router.GET("/user", func(c *gin.Context) {
firstname := c.DefaultQuery("name", "kim") // 获取query中的name,没有的话就为kim
lastname := c.Query("age") // 获取query中的age
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
multipart/urlencoded form参数:
func main() {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("age")
nick := c.DefaultPostForm("name", "kim")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
模型绑定与参数校验:
我们已经见识了x-www-form-urlencoded类型的参数处理,现在越来越多的应用习惯使用JSON来通信,也就是无论返回的response还是提交的request,其content-type类型都是application/json的格式。而对于一些旧的web表单页还是x-www-form-urlencoded的形式,这就需要我们的服务器能改hold住这多种content-type的参数了。
由于go是静态语言,需要先实现定义数据模型,这就需要用到gin的model bind功能了。
gin使用go-playground/validator.v8验证参数,查看完整文档。
需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置json:"fieldname" 。
此外,Gin还提供了两套绑定方法:
- Must bind
- Methods -
Bind,BindJSON,BindXML,BindQuery,BindYAML - Behavior - 这些方法底层使用
MustBindWith,如果存在绑定错误,请求将被以下指令中止c.AbortWithError(400, err).SetType(ErrorTypeBind),响应状态代码会被设置为400,请求头Content-Type被设置为text/plain; charset=utf-8。注意,如果你试图在此之后设置响应代码,将会发出一个警告[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行为,请使用ShouldBind相关的方法
- Methods -
- Should bind
- Methods -
ShouldBind,ShouldBindJSON,ShouldBindXML,ShouldBindQuery,ShouldBindYAML - Behavior - 这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。
- Methods -
当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith。
你还可以给字段指定特定规则的修饰符,如果一个字段用binding:"required"修饰,并且在绑定时该字段的值为空,那么将返回一个错误。
package main import (
"net/http" "github.com/gin-gonic/gin"
) type Person struct {
Name string `json:"name" binding:"required"` // json格式从name取值,并且该值为必须的
Age int `json:"age" binding:"required,gt=20"` // json格式从age取值,并且该值为必须的,且必须大于20
} func main() { router := gin.Default() router.POST("/test", func(context *gin.Context) {
var person Person
// var person = Person{
// Name: "default", // 设置参数默认值, 若不存在该值的时候
// }
// 这里我确定传过来的一定是JSON所以用ShouldBindJSON,否则可以用ShouldBind
if err := context.ShouldBindJSON(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}) router.Run(":3000")
}
自定义验证器:
package main import (
"net/http"
"reflect"
"time" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
) type Booking struct {
// 这里的验证方法为bookabledate
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield=CheckIn表示大于的字段为CheckIn
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
} func bookableDate(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
// 这里有两个知识点,映射和断言
// 在这里,field是一个reflect.Type的接口类型变量,通过Interface方法获得field接口类型变量的真实类型,可以理解为reflect.Value的逆操作
// 在这里,断言就是将一个接口类型的变量转化为time.Time,前提是后者必须实现了前者的接口
// 综上,这里就是将field进行了类型转换
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
} func main() {
route := gin.Default() // 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
} route.GET("/bookable", getBookable)
route.Run(":8085")
} func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
中间件:
中间件可以在我们接受到一个http请求时,在handle之前或者handle之后做一些处理。通常,在handle之前,我们可以通过中间件很方便地进行校验,如果再handle之后,我们可以对response进行一些调整。
// 创建一个不包含中间件的路由器
gin.New()
// 使用自定义中间件或者gin提供的中间件
gin.use(gin.Logger()) 可代替:
gin.Default()
其实gin默认使用了Logger和Recovery两个中间件,然后也是在内部调用了New:
// gin.go
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery()) // 使用了Logger和Recovery两个中间件
return engine
}
我们对这两个中间件做一个简单的了解:
- Logger中间件可以让我们做打印的一些自定义配置
- Recovery中间件可以让我们从崩溃中恢复
func main() {
logfile, _ := os.Create("./logs/gin.log")
// 这里将log输出到指定文件
// 注意这个配置一定要在gin.Default()之前
gin.DefaultWriter = io.MultiWriter(logfile, os.Stdout)
router := gin.Default()
// 这里分别使用两个中间件
router.Use(gin.Logger())
router.Use(gin.Recovery())
router.POST("/test", func(context *gin.Context) {
var person Person
if err := context.ShouldBind(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
自定义中间件:
// recovery.go 这里只要返回一个HandlerFunc类型即可
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
} // gin.go HandlerFunc就是一个参数为*context的函数
type HandlerFunc func(*Context)
我们来写一个IP鉴权的中间件,假设我们的需求是只有白名单中的ip才可以访问服务器,那么我们可以这么实现:
ipauth.go
// ipauth.go
func Auth() gin.HandlerFunc {
return func(context *gin.Context) {
// 定义ip白名单
whiteList := []string{
"127.0.0.1",
}
ip := context.ClientIP()
flag := false
for _, host := range whiteList {
if ip == host {
flag = true
break
}
}
if !flag {
context.String(http.StatusNetworkAuthenticationRequired, "your ip is not trusted: %s", ip)
context.Abort()
}
}
}
main.go
// main.go
func main() {
router := gin.New()
router.Use(ipauth.Auth())
router.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
Group中使用中间件
此外,我们的中间件可以不全局使用,而只针对部分的group:
func main() {
router := gin.Default()
// 定义了group
authorized := router.Group("/auth", ipauth.Auth())
// 对上面这个group进行路由绑定
authorized.GET("/write", handle)
router.GET("/read", handle)
router.Run(":3000")
}
func handle(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}
单个路由使用中间件:
func main() {
router := gin.Default()
// 注册一个路由,使用了 middleware1,middleware2 两个中间件
router.GET("/someGet", middleware1, middleware2, handler)
// 默认绑定 :8080
router.Run()
}
func handler(c *gin.Context) {
log.Println("exec handler")
}
func middleware1(c *gin.Context) {
log.Println("exec middleware1")
//你可以写一些逻辑代码
// 执行该中间件之后的逻辑
c.Next()
}
func middleware2(c *gin.Context) {
log.Println("arrive at middleware2")
// 执行该中间件之前,先跳到流程的下一个方法
c.Next()
// 流程中的其他逻辑已经执行完了
log.Println("exec middleware2")
//你可以写一些逻辑代码
}
可以看出,中间件的写法和路由的 Handler 几乎是一样的,只是多调用c.Next()。正是有个c.Next(),我们可以在中间件中控制调用逻辑的变化,看下面的 middleware2 代码。在 middleware2中,执行到 c.Next()时,Gin 会直接跳到流程的下一个方法中,等到这个方法执行完后,才会回来接着执行 middleware2 剩下的代码。
所以请求上面注册的路由 url /someGet ,请求先到达middleware1,然后到达 middleware2,但此时 middleware2调用了 c.Next(),所以 middleware2的代码并没有执行,而是跳到了 handler ,等 handler执行完成后,跳回到 middleware2,执行 middleware2剩下的代码。
在中间件中使用goroutines
在中间件或处理程序中启动新的goroutine时,你不应该使用其中的原始上下文,你必须使用只读副本
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// 创建要在goroutine中使用的副本
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// 这里使用你创建的副本
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.Run(":3000")
}
中间件中的程序终端Abort
func Auth() {
return func(c *gin.Context){
fmt.Println("before...")
c.Abort() // 终端当前请求,不会执行到controller层中具体逻辑, 其他中间件仍会执行,和该中间件中之后的逻辑
return // 当前中间件中剩余逻辑不会被执行
fmt.Println("after...")
}
}
[日志设置]
使用:
//f, err := os.Create("gin.log") // 重启之后会清空该文件
f, err := os.OpenFile("gin.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
//fErr, _ := os.Create("gin-err.log") // 重启之后会清空该文件
// 内部发生panic错误的时候,并且没有进行异常捕获的情况下,会将其输入到该文件, 若存在异常中间件的话,则自定义进行处理即可,并不会输出到该文件中了
fErr, err := os.OpenFile("gin-err.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
gin.DefaultErrorWriter = io.MultiWriter(fErr, os.Stderr)
// 设置运行模式
gin.SetMode(cfg.App.RunMode)
// HTTP Server
handler := gin.New()
...
[运行模式]
系统支持三种运行模式: debug,test,release
debug: 会输出所有信息, 包含启动服务的时候的提示信息以及路由信息等
test:不会输出服务启动信息与路由信息, 之后输出路由访问信息
release: 显示信息与test模式的一致
判断运行模式方式:
func IsRelease() bool {
return gin.Mode() == gin.ReleaseMode
Golang框架之gin的更多相关文章
- 01 . Go框架之Gin框架从入门到熟悉(路由和上传文件)
Gin框架简介 Gin是使用Go/Golang语言实现的HTTP Web框架, 接口简洁, 性能极高,截止1.4.0版本,包含测试代码,仅14K, 其中测试代码9K, 也就是说测试源码仅5k左右, 具 ...
- 01 . Go之从零实现Web框架(类似Gin)
设计一个框架 大部分时候,我们需要实现一个 Web 应用,第一反应是应该使用哪个框架.不同的框架设计理念和提供的功能有很大的差别.比如 Python 语言的 django和flask,前者大而全,后者 ...
- Web框架之Gin
Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 如果你是性能和高效的追求者, 你会爱上Gin. ...
- Web框架之Gin介绍及使用
Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 如果你是性能和高效的追求者, 你会爱上Gin. ...
- 1.1 安装gin框架&使用gin编写简单服务端
01.安装gin框架 1)go环境配制 a)配制环境变量 GOPATH修改为go的工作文件夹路径 D:\Golang\goproject GOROOT修改为go的安装路径 D:\Golang\go1. ...
- golang 框架 之 CHI
1,特性 golang 的 http框架已经有很多,究其原因:go 的net/http 已经做了大部分工作,封装一个框架的工作量,没有那么的大. 不过呢,林子大了鸟也就多了,部分框架的代码质量实在不敢 ...
- Golang框架beego和bee的开发使用
Golang语言简洁.明细,语法级支持协程.通道.err,非常诱惑人.平时也看了看Golang的语法,正苦于没有需求,我想把beego的源码搬过来看看. 首先,第一步:beego环境的搭建 在我之前看 ...
- Go语言基础之20--web编程框架之Gin框架
一.Gin框架介绍 1.1 简介 A. 基于httprouter开发的web框架. http://github.com/julienschmidt/httprouter B. 提供Martini风格的 ...
- Golang框架beego电影网爬虫小试牛刀
学习了一段时间golang,又参考课程学习了beego开发网站爬虫,项目的目录结构是: 采集的目标是豆瓣网电影,入口地址是:https://movie.douban.com/subject/19008 ...
- Golang框架Beego在Windows环境下小试牛刀
Beego官网beego官网 : https://beego.me/github : https://github.com/beego Beego安装前提: ①Go 1.1+ 以确保所有功能的正常使用 ...
随机推荐
- 6.2 XXE和XML利用
pikaqu靶场xml数据传输测试-有回显,玩法,协议,引入 1.构造payload 写文件 <?xml version="1.0" encoding="UTF-8 ...
- XGBoost模型 0基础小白也能懂(附代码)
XGBoost模型 0基础小白也能懂(附代码) 原文链接 啥是XGBoost模型 XGBoost 是 eXtreme Gradient Boosting 的缩写称呼,它是一个非常强大的 Boostin ...
- .NET 6.0 + WPF 使用 Prism 框架实现导航
前言 Prism 一个开源的框架,专门用于开发可扩展.模块化和可测试的企业级 XAML 应用程序,适用于 WPF(Windows Presentation Foundation)和 Xamarin F ...
- C# HttpClient 基本使用方式(一)
.NetCore主要提供了HttpWebRequest,WebClient,HttpClient这三种访问web的方式,其中HttpWebRequest,WebClient都在官方被标注为已过时,如果 ...
- 所见即所得,赋能RAG:PDF解析里的段落识别
前几天,有一位用户使用OCR产品识别多栏论文后向我们询问:要怎么解决不合适的断句.分段以及错误阅读顺序的问题? 我们用一个相似案例为大家直观展示这位用户遇到的情况. 如图中的多栏期刊,如果用OCR识别 ...
- Java怎么把多个对象的list的数据合并
1.示例一:创建几个包含Person对象的List,并将它们合并成一个新的List 在Java中,将多个对象的List合并通常涉及到遍历这些List并将它们的元素添加到一个新的List中.这里,我将给 ...
- CTC联结时间分类算法: 连接主义时间分类: 用递归神经网络标记未分割序列数据《Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks》(语音、文本识别、RNN端到端训练序列数据的方法)
11月6号例会汇报. 糊弄的看了一个算法-CTC算法,没有汇报论文,因为没看论文(我导知道后,应该会锤死我...) 当然,汇报完之后,被我导腾讯会议通过网线批了我一顿,PPT做的太烂了!字太多,听不到 ...
- 处理英文中的单数复数 (pluralize, singular plural)
因为英语很烂, 有时候很烦这个. 如果是 hard code 的情况, 如果我不清楚的话就会去这里找 https://www.wordhippo.com/what-is/the-plural-of/l ...
- 【赵渝强老师】MongoDB的inMemory存储引擎
一.MongoDB的存储引擎概述 存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上.从MongoDB 3.2 版本开始 ...
- 进程D 状态的产生及原因解释
在 Linux 系统中,进程的 D 状态表示进程处于不可中断的睡眠状态 (Uninterruptible Sleep).这种状态通常由进程等待某些资源或事件引起,这些资源或事件无法立即可用.以下是一些 ...