Go 语言的路由库有很多,性能上都大同小异,最大的差别应该是路由函数的签名,官网采纳了 http.Handler 接口,而大部分非官方路由都将 http.ResponseWriterhttp.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.ResponseWriterhttp.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...