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)gofunc(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注册路由类似,此外它本身也可以注册新的路由组为了规范,在注册路由组下的路由时,通常用
{}括住:gog := 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/cors- go
cors.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 } }) 常用的配置项和一般的网页应用一样:
goimport "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等,直接替换掉默认的日志记录器即可:gogin.DefaultWriter = sugar.Writer() gin.DefaultErrorWriter = sugar.Writer()如果不想使用
zap的sugar,可以手动实现一个适配器gotype 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 }