Gin: 路由
Gin路由
httprouter
特点
Gin的路由基于julienschmidt/httprouter,是一个轻量级的路由组建(总共三个文件一千余行代码)httprouter只提供路由功能,标准库中的HandleFunc()依赖ServeMux来路由,而ServeMux针对前缀匹配采用遍历的方式匹配,效率较差httprouter提供:请求方法和路径分离,路由算法优化(基于基数树)因此一次路由极快,路径模糊匹配,路径参数捕获- 那么
httprouter.Router就可以作为一个前端控制器直接传递给http.ListenAndServe(path, handler)了
API
httprouter.New():获取路由器对象*httprouter.Router提供一系列方法绑定路由(仅仅处理路由)Handle(method, path string, handle Handle):将方法、路径绑定到处理器,其中handle为httprouter.HandleGET/POST/PUT/DELETE/PATCH/OPTIONS/HEAD(path string, handle Handle):Handle()的快捷方法Handler()/HandlerFunc():net/http的适配器
- 路径参数捕获:路径中使用
:可以捕获,例如/users/:uid,将不包含路径分隔符的路径参数捕获并赋值给uid- 使用
httprouter.Handle:Handle方法的签名就是func(http.ResponseWriter, *http.Request, Params),直接使用Params参数来访问 - 使用
http的处理器API:则适配器会将参数挂到*http.Request.Context()中,通过httprouter.ParamsFromContext(Context)获取Params对象
- 使用
- 还可以使用
*捕获包含路径分隔符的路径参数,因此它们只能放在最后,例如/*userinfo - 当模糊匹配和精确匹配发生冲突时,按精确匹配大于
:大于*的顺序进行匹配 Params提供ByName(string)获取键对应的值
Gin的参数解析
gin.Default(...OptionFunc) *Engine:获取一个*gin.Engine,默认配置即可,可以通过选项模式修改配置Gin支持路由参数,和httprouter的使用方法类似*gin.Engine对象提供和*httprouter.Router类似的方法:Handle()/GET()/...路径匹配规则类似:精确匹配、
:匹配、*匹配区别在于处理器函数的类型是
HandlerFunc:func(*gin.Context)1
2
3func(http.ResponseWriter, *http.Request, Params)
func(*gin.Context)将参数替换成了上下文
因此获取参数也变成了:
*gin.Context.Param(string)
Gin还支持捕获URL参数:*gin.Context.Query(string):获取GET参数的值,若无返回空*gin.Context.DefaultQuery(string, string):允许自定义默认值
Gin还支持捕获请求体参数*gin.Context.PostForm(string):可以解析表单参数application/x-www-form-urlencoded与multipart/form-data*gin.Context.ShouldBind(any):自动选择解析器,并赋值给传入的参数,因此传入的参数应该是指针,不会进行错误处理*gin.Context.Bind(any):是对ShouldBind()的封装,但如果参数解析错误则会自动返回400错误*gin.Context.MustBindWith(any, binding.Binding):指定解析器,和Bind()一样会自动返回400错误BindJSON()/BindQuery()/BindYAML()/BindXML()等:MustBindWith()的快捷函数
- 对于
JSON、XML、ProtoBuf等数据,ShouldBind()只能调用一次,需要使用ShouldBindBodyWith()方法
分组路由
Gin提供*gin.Engine.Group(relativePath string, handlers ...HandlerFunc) *RouterGroup方法分组relativePath的捕获语法与httprouter一致,相当于分组所表示的前缀可以表示相对路径(即开头不带
/),则相对于Engine等价于相对于根路径,相对于RouterGroup等价于相对于路由组的路径handlers会在子路由处理器执行之前执行,它们不是relativePath对应的处理器,如果需要则应该在分组内绑定一个""路由*RouterGroup的方法和*Engine注册路由类似,此外它本身也可以注册新的路由组为了规范,在注册路由组下的路由时,通常用
{}括住:1
2
3
4
5g := e.Group("v1")
{
g.GET("/xxx", func(c *gin.Context) {
})
}
错误处理器
- 针对
404路由响应(没有对应的路由),可以自定义处理器:*gin.Engine.NoRoute(HandlerFunc) - 针对
405路由响应(有对应的路由但是没有匹配的请求方法),可以使用*gin.Engine.NoMethod(HandlerFunc) gin本身没有十分完整的捕获错误、统一处理,不像spring-mvc的@ControllerAdvice,但是可以通过中间件实现- 但是可以使用中间件,
gin的中间件引入非常灵活,扩展性很高,完全可以实现类似统一错误处理器的效果
CORS中间件
官方提供了
cors的中间件github.com/gin-contrib/cors1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62cors.New(cors.Config{
type Config struct {
AllowAllOrigins bool
// AllowOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// Default value is []
AllowOrigins []string
// AllowOriginFunc is a custom function to validate the origin. It takes the origin
// as an argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowOrigins is ignored.
AllowOriginFunc func(origin string) bool
// Same as AllowOriginFunc except also receives the full request context.
// This function should use the context as a read only source and not
// have any side effects on the request, such as aborting or injecting
// values on the request.
AllowOriginWithContextFunc func(c *gin.Context, origin string) bool
// AllowMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS)
AllowMethods []string
// AllowPrivateNetwork indicates whether the response should include allow private network header
AllowPrivateNetwork bool
// AllowHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
AllowHeaders []string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// ExposeHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposeHeaders []string
// MaxAge indicates how long (with second-precision) the results of a preflight request
// can be cached
MaxAge time.Duration
// Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com
AllowWildcard bool
// Allows usage of popular browser extensions schemas
AllowBrowserExtensions bool
// Allows to add custom schema like tauri://
CustomSchemas []string
// Allows usage of WebSocket protocol
AllowWebSockets bool
// Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed
AllowFiles bool
// Allows to pass custom OPTIONS response status code for old browsers / clients
OptionsResponseStatusCode int
}
})常用的配置项和一般的网页应用一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import "github.com/gin-contrib/cors"
func A() {
cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:8000"},
AllowMethods: []string{"*"},
// 允许客户端发送的请求头
AllowHeaders: []string{
"Origin",
"X-Requested-With",
"Content-Type",
"Accept",
"Authorization",
},
// 允许客户端读取的响应头
ExposeHeaders: []string{
"Content-Length",
"Access-Control-Allow-Origin",
"Access-Control-Allow-Headers",
"Cache-Control",
"Content-Language",
"Content-Type",
},
// 允许携带 cookie
AllowCredentials: true,
})
}
日志重定向
gin本身直接使用Stdout和Stderr,如果需要集成zap等,直接替换掉默认的日志记录器即可:1
2gin.DefaultWriter = sugar.Writer()
gin.DefaultErrorWriter = sugar.Writer()如果不想使用
zap的sugar,可以手动实现一个适配器1
2
3
4
5
6
7
8
9
10type zapWriter struct {
logger *zap.Logger
}
func (w *zapWriter) Write(p []byte) (n int, err error) {
w.logger.Info(string(p))
return len(p), nil
}
gin.DefaultWriter = &zapWriter{ logger:logger }