Go 语言的路由库有很多,性能上都大同小异,最大的差别应该是路由函数的签名,官网采纳了 http.Handler
接口,而大部分非官方路由都将 http.ResponseWriter
和 http.Request
合并成了一个对象。本文介绍的库 https://github.com/issue9/mux 利用 go1.18 对泛型的反对,实现了用户自定义该性能的需要,仅须要几步即可实现一个欠缺的路由,实用于疾速开发一个 web 框架。
如何应用
以实现一个兼容 http.Handler
接口的路由为例,仅需以下几个步骤即可。
定义路由解决类型
能够是接口,也能够是函数,咱们以 http.Handler
为例,那么该类型就是 http.Handler
接口,此步能够省略。
定义泛型对应的类型
以 http.Handler
为参数实例化泛型类型:
package custom_routerimport "github.com/issue9/mux/v6"type ( Router = mux.RouterOf[http.Handler] Prefix = mux.PrefixOf[http.Handler] Resource = mux.ResourceOf[http.Handler] Options = mux.OptionsOf[http.Handler] Middleware = mux.MiddlewareOf[http.Handler])
定义 New 函数
咱们须要一个 CallOf
函数,用于将给定的参数转换成调用 http.Handler
的办法。其原型如下:
CallOf[T any] func(http.ResponseWriter, *http.Request, Params, T)
New
能够间接调用 NewRouterOf
办法,给出 CallOf
的实例化办法即可。
package custom_routerimport "github.com/issue9/mux/v6"func call(w http.ResponseWriter, r *http.Request, ps Params, h http.Handler) { h.ServeHTTP(w, WithValue(r, ps))}func New(name string, o *Options) *Router { return NewRouterOf[http.Handler](name, call, o)}
辅助函数
而后定义一些辅助函数,比方将参数写入到 http.Request
和从 http.Request
中获取参数。
package custom_routerimport "github.com/issue9/mux/v6"type contextKey intconst contextKeyParams contextKey = 0// GetParams 获取以后申请实例上的参数列表func GetParams(r *http.Request) mux.Params { if ps := r.Context().Value(contextKeyParams); ps != nil { return ps.(Params) } return nil}// WithValue 将参数 ps 附加在 r 上func WithValue(r *http.Request, ps mux.Params) *http.Request { if ps == nil || ps.Count() == 0 { return r } if ps2 := GetParams(r); ps2 != nil && ps2.Count() > 0 { ps2.Range(func(k, v string) { ps.Set(k, v) }) } return r.WithContext(context.WithValue(r.Context(), contextKeyParams, ps))}
这样一个兼容 http.Handler
的路由就实现了,之后就能够失常应用路由。
它反对一般字符串匹配,也反对以 {name:rule}
模式的匹配,其中 rule 能够是正则表达式或空值。具体的语法能够参考 https://github.com/issue9/mux。
package custom_routerfunc main() { r := New("", &Options{}) r.Get("/users/{id:\\d+}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ // TODO }))}
一个更简单的例子
上面定义了一个新路由,摒弃了官网的 http.ResponseWriter
和 http.Request
,采纳 Context
对象传递上下文所需的内容。所以咱们自定义了一个 Handler
接口用以代替官网的 http.Handler
接口,其 Handle
办法的参数只接管 Context
对象。
对于 New
办法也不再是间接传递 *options
对象,而是以可选的函数办法的模式传递。
package custom_routerimport "github.com/issue9/mux/v6"type ( Context struct { R *http.Request W http.ResponseWriter P mux.Params } Handler interface { Handle(*Context) } HandlerFunc func(*Context) Router = mux.RouterOf[Handler] Prefix = mux.PrefixOf[Handler] Resource = mux.ResourceOf[Handler] Middleware = mux.MiddlewareOf[Handler] options = mux.OptionsOf[Handler] Option func(o *options))func (f HandlerFunc) Handle(c *Context) { f(c) }func call(w http.ResponseWriter, r *http.Request, ps mux.Params, h Handler) { h.Handle(&Context{R: r, W: w, P: ps})}func New(name string, o ...Option) *Router { opt := &options{} for _, oo := range o { oo(opt) } return NewRouterOf[Handler](name, call, opt)}// 一些实现 Option 的函数,整个 options 的内容都能够采纳此形式设置。func Lock(o *options) { o.Lock = true}func Unlock(o *options) { o.Lock = false}func NotFound(f http.HandlerFunc) Option { if f == nil { f = http.NotFound } return func(o *Options) { o.NotFound = f }}
之后就能够失常应用路由:
package custom_routerfunc main() { r := New("") r.Get("/users/{id:\\d+}", HandlerFunc(func(ctx *Context){ // TODO }))}
性能
无关性能能够参考 https://caixw.github.io/go-ht... 提供了基于 http.Handler
的性能测试。
原文地址:https://caixw.io/posts/2022/b...