mux

package module
v9.0.0-beta.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 31, 2024 License: MIT Imports: 17 Imported by: 4

README

mux

Go Go version Go Report Card license codecov PkgGoDev

这是一个用于定制路由的包,适用于第三方框架实现自己的路由功能。想直接使用,需要少量的代码实例化泛型对象。

所有实现的路由都支持以下功能:

  • 路由参数;
  • 支持正则表达式作为路由项匹配方式;
  • 拦截正则表达式的行为;
  • 自定义的 OPTIONS 请求处理方式;
  • 自动生成 HEAD 请求处理方式;
  • 根据路由反向生成地址;
  • 任意风格的路由,比如 discuz 这种不以 / 作为分隔符的;
  • 分组路由,比如按域名,或是版本号等;
  • CORS 跨域资源的处理;
  • 支持中间件;
  • 支持 OPTIONS * 请求;
  • TRACE 请求方法的支持;
  • panic 处理;
import "github.com/issue9/mux/v9"

router := mux.NewRouter[http.Handler]("", ...) // 采用泛型实现自定义对象
router.Get("/users/1", h).
    Post("/login", h).
    Get("/pages/{id:\\d+}.html", h). // 匹配 /pages/123.html 等格式,path = 123
    Get("/posts/{path}.html", h).    // 匹配 /posts/2020/11/11/title.html 等格式,path = 2020/11/11/title

// 统一前缀路径的路由
p := router.Prefix("/api")
p.Get("/logout", h) // 相当于 m.Get("/api/logout", h)
p.Post("/login", h) // 相当于 m.Get("/api/login", h)

// 对同一资源的不同操作
res := p.Resource("/users/{id:\\d+}")
res.Get(h)   // 相当于 m.Get("/api/users/{id:\\d+}", h)
res.Post(h)  // 相当于 m.Post("/api/users/{id:\\d+}", h)
res.URL(map[string]string{"id": "5"}) // 构建一条基于此路由项的路径:/users/5

http.ListenAndServe(":8080", router)

语法

路由参数采用大括号包含,内部包含名称和规则两部分:{name:rule}, 其中的 name 表示参数的名称,rule 表示对参数的约束规则。

name 可以包含 - 前缀,表示在实际执行过程中,不捕获该名称的对应的值, 可以在一定程序上提升性能。

rule 表示对参数的约束,一般为正则或是空,为空表示匹配任意值, 拦截器一栏中有关 rule 的高级用法。以下是一些常见的示例。

/posts/{id}.html                  // 匹配 /posts/1.html
/posts-{id}-{page}.html           // 匹配 /posts-1-10.html
/posts/{path:\\w+}.html           // 匹配 /posts/2020/11/11/title.html
/tags/{tag:\\w+}/{path}           // 匹配 /tags/abc/title.html
路径匹配规则

可能会出现多条记录与同一请求都匹配的情况,这种情况下, 系统会找到一条认为最匹配的路由来处理,判断规则如下:

  1. 普通路由优先于正则路由;
  2. 拦截器优先于正则路由;
  3. 正则路由优先于命名路由;

比如:

/posts/{id}.html              // 1
/posts/{id:\\d+}.html         // 2
/posts/1.html                 // 3

/posts/1.html      // 匹配 3
/posts/11.html     // 匹配 2
/posts/index.html  // 匹配 1

路径的匹配是以从左到右的顺序进行的,父节点不会因为没有子节点匹配而扩大自己的匹配范围, 比如 /posts/{id}-{page:digit}.html 可以匹配 /posts/1-1.html,但无法匹配 /posts/1-1-1.html,虽然理论上 1-1- 也能匹配 {id},但是 1- 已经优先匹配了, 在子元素找不到的情况下,并不会将父元素的匹配范围扩大到 1-1-

路由参数

通过正则表达式匹配的路由,其中带命名的参数可通过 GetParams() 获取:

import "github.com/issue9/mux/v9"

params := mux.GetParams(r)

id, err := params.Int("id")
 // 或是
id := params.MustInt("id", 0) // 在无法获取 id 参数时采用 0 作为默认值返回

高级用法

分组路由

可以通过匹配 Matcher 接口,定义了一组特定要求的路由项。

// server.go

import "github.com/issue9/mux/v9"
import "github.com/issue9/mux/v9/muxutil"

m := mux.NewRouters(...)

def := mux.NewRouter("default")
m.AddRouter(muxutil.NewPathVersion("version-key", "v1"), def)
def.Get("/path", h1)

host := mux.NewRouter("host")
m.AddRouter(muxutil.NewHosts("*.example.com"), host)
host.Get("/path", h2)

http.ListenAndServe(":8080", m)

// client.go

// 访问 h2 的内容
r := http.NewRequest(http.MethodGet, "https://abc.example.com/path", nil)
r.Do()

// 访问 h1 的内容
r := http.NewRequest(http.MethodGet, "https://other_domain.com/v1/path", nil)
r.Do()
拦截器

正常情况下,/posts/{id:\d+} 或是 /posts/{id:[0-9]+} 会被当作正则表达式处理, 但是正则表达式的性能并不是很好,这个时候我们可以通过在 NewRouter 传递 WithInterceptor 进行拦截:

import "github.com/issue9/mux/v9"

func digit(path string) bool {
    for _, c := range path {
        if c < '0' || c > '9' {
            return false
        }
    }
    return len(path) > 0
}

// 路由中的 \d+ 和 [0-9]+ 均采用 digit 函数进行处理,不再是正则表达式。
interceptor := mux.WithInterceptor(digit, "\\d+", "[0-9]+")
r := mux.NewRouter(..., interceptor)

这样在所有路由项中的 [0-9]+\\d+ 将由 digit 函数处理, 不再会被编译为正则表达式,在性能上会有很大的提升。 通过该功能,也可以自定义一些非正常的正则表达这式,然后进行拦截,比如:

/posts/{id:digit}/.html

如果不拦截,最终传递给正则表达式,可能会出现编译错误,通过拦截器可以将 digit 合法化。 目前提供了以下几个拦截器:

  • DigitInterceptor 限定为数字字符,相当于正则的 [0-9]
  • WordInterceptor 相当于正则的 [a-zA-Z0-9]
  • AnyInterceptor 表示匹配任意非空内容;

用户也可以自行实现 InterceptorFunc 作为拦截器。具体可参考 Interceptor 的介绍。

CORS

CORS 不再是以中间件的形式提供,而是通过 NewRouter 直接传递有关 CORS 的配置信息, 这样可以更好地处理每个地址支持的请求方法。

OPTIONS 请求方法由系统自动生成。

import "github.com/issue9/mux/v9"

r := mux.NewRouter(... ,mux.CORS(...)) // 任意跨域请求
r.Get("/posts/{id}", nil)     // 默认情况下, OPTIONS 的报头为 GET, OPTIONS
http.ListenAndServe(":8080", m)

// client.go

// 访问 h2 的内容
r := http.NewRequest(http.MethodGet, "https://localhost:8080/posts/1", nil)
r.Header.Set("Origin", "https://example.com")
r.Do() // 跨域,可以正常访问


r = http.NewRequest(http.MethodOptions, "https://localhost:8080/posts/1", nil)
r.Header.Set("Origin", "https://example.com")
r.Header.Set("Access-Control-Request-Method", "GET")
r.Do() // 预检请求,可以正常访问
自定义路由

官方提供的 http.Handler 未必是符合每个人的要求,通过 Router 用户可以很方便地实现自定义格式的 http.Handler, 只需要以下几个步骤:

  1. 定义一个专有的路由处理类型,可以是类也可以是函数;
  2. 根据此类型,生成对应的 Router、Prefix、Resource、MiddlewareFunc 等类型;
  3. 定义 CallFunc 函数;
  4. 将 CallFunc 传递给 NewRouter;
type Context struct {
    *http.Request
    W http.ResponseWriter
    P Params
}

type HandlerFunc func(ctx *Context)

type Router = Router[HandlerFunc]
type Prefix = Prefix[HandlerFunc]
type Resource = Resource[HandlerFunc]
type MiddlewareFunc = MiddlewareFunc[HandlerFunc]
type Middleware = Middleware[HandlerFunc]

func New(name string)* Router {
    call := func(w http.ResponseWriter, r *http.Request, ps Params, h HandlerFunc) {
        ctx := &Context {
            R: r,
            W: w,
            P: ps,
        }
        h(ctx)
    }
    opt := func(n types.Node) Handler {
        return HandlerFunc(func(ctx* Context){
            ctx.W.Header().Set("Allow", n.AllowHeader())
        })
    }

    m := func(n types.Node) Handler {
        return HandlerFunc(func(ctx* Context){
            ctx.W.Header().Set("Allow", n.AllowHeader())
            ctx.W.WriteHeader(405)
        })
    }
    notFound func(ctx* Context) {
        ctx.W.WriteHeader(404)
    }
    return NewRouter[HandlerFunc](name, f, notFound, m, opt)
}

以上就是自定义路由的全部功能,之后就可以直接使用:

r := New("router", nil)

r.Get("/path", func(ctx *Context){
    // TODO
    ctx.W.WriteHeader(200)
})

r.Prefix("/admin").Get("/login", func(ctx *Context){
    // TODO
    ctx.W.WriteHeader(501)
})

更多自定义路由的介绍可参考 https://caixw.io/posts/2022/build-go-router-with-generics.html 或是 examples 下的示例。

性能

https://caixw.github.io/go-http-routers-testing/ 提供了与其它几个框架的对比情况。

版权

本项目采用 MIT 开源授权许可证,完整的授权说明可在 LICENSE 文件中找到。

Documentation

Overview

Package mux 适用第三方框架实现可定制的路由

语法

路由参数采用大括号包含,内部包含名称和规则两部分:`{name:rule}`, 其中的 name 表示参数的名称,rule 表示对参数的约束规则。

name 可以包含 `-` 前缀,表示在实际执行过程中,不捕获该名称的对应的值, 可以在一定程序上提升性能。

rule 表示对参数的约束,一般为正则或是空,为空表示匹配任意值, 拦截器一栏中有关 rule 的高级用法。以下是一些常见的示例。

/posts/{id}.html                  // 匹配 /posts/1.html
/posts-{id}-{page}.html           // 匹配 /posts-1-10.html
/posts/{path:\\w+}.html           // 匹配 /posts/2020/11/11/title.html
/tags/{tag:\\w+}/{path}           // 匹配 /tags/abc/title.html

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnyMethods

func AnyMethods() []string

AnyMethods 返回 Router.Any 中添加的请求方法

func CheckSyntax

func CheckSyntax(pattern string) error

CheckSyntax 检测路由项的语法格式

func Methods

func Methods() []string

Methods 返回库支持的请求方法

func Trace

func Trace(w http.ResponseWriter, r *http.Request, body bool)

Trace 一种简单的处理 TRACE 请求的方法

可以结合 WithTrace 处理。

func URL

func URL(pattern string, params map[string]string) (string, error)

URL 根据参数生成地址

pattern 为路由项的定义内容; params 为路由项中的参数,键名为参数名,键值为参数值。

NOTE: 仅仅是将 params 填入到 pattern 中, 不会判断参数格式是否正确。

Types

type CallFunc

type CallFunc[T any] func(http.ResponseWriter, *http.Request, types.Route, T)

CallFunc 指定如何调用用户给定的类型 T

type Group

type Group[T any] struct {
	// contains filtered or unexported fields
}

Group 管理一组 Router

func NewGroup

func NewGroup[T any](
	call CallFunc[T],
	notFound T,
	methodNotAllowedBuilder, optionsBuilder types.BuildNodeHandler[T],
	o ...Option,
) *Group[T]

NewGroup 声明 Group 对象

初始化参数与 NewRouter 相同,这些参数最终也会被 Group.New 传递给新对象。

func (*Group[T]) Add

func (g *Group[T]) Add(matcher Matcher, r *Router[T])

Add 添加路由

matcher 用于判断进入 r 的条件,如果为空,则表示不作判断。 如果有多个 matcher 都符合条件,第一个符合条件的 r 获得优胜;

func (*Group[T]) New

func (g *Group[T]) New(name string, matcher Matcher, o ...Option) *Router[T]

New 声明新路由

新路由会继承 NewGroup 中指定的参数,其中的 o 可以覆盖由 NewGroup 中指定的相关参数;

func (*Group[T]) Remove

func (g *Group[T]) Remove(name string)

func (*Group[T]) Router

func (g *Group[T]) Router(name string) *Router[T]

Router 返回指定名称的路由

func (*Group[T]) Routers

func (g *Group[T]) Routers() []*Router[T]

Routers 返回路由列表

func (*Group[T]) Routes

func (g *Group[T]) Routes() map[string]map[string][]string

func (*Group[T]) ServeHTTP

func (g *Group[T]) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Group[T]) Use

func (g *Group[T]) Use(m ...types.Middleware[T])

Use 为所有已经注册的路由添加中间件

type Hosts

type Hosts struct {
	// contains filtered or unexported fields
}

Hosts 限定域名的匹配工具

func NewHosts

func NewHosts(lock bool, domain ...string) *Hosts

NewHosts 声明新的 Hosts 实例

func (*Hosts) Add

func (hs *Hosts) Add(domain ...string)

Add 添加新的域名

域名的格式和路由的语法格式是一样的,比如:

api.example.com
{sub:[a-z]+}.example.com

如果存在命名参数,也可以通过也可通过 types.Params 接口获取。 当语法错误时,会触发 panic,可通过 CheckSyntax 检测语法的正确性。

func (*Hosts) Delete

func (hs *Hosts) Delete(domain string)

func (*Hosts) Match

func (hs *Hosts) Match(r *http.Request, ctx *types.Context) bool

func (*Hosts) RegisterInterceptor

func (hs *Hosts) RegisterInterceptor(f InterceptorFunc, name ...string)

RegisterInterceptor 注册拦截器

NOTE: 拦截器只有在注册之后添加的域名才有效果。

type InterceptorFunc

type InterceptorFunc = syntax.InterceptorFunc

type Matcher

type Matcher interface {
	// Match 验证请求是否符合当前对象的要求
	//
	// 如果返回 false,那么不应当对参所指向的内容作修改,否则可能影响后续的判断。
	Match(*http.Request, *types.Context) bool
}

Matcher 验证一个请求是否符合要求

Matcher 用于路由项的前置判断,用于对路由项进行归类, 符合同一个 Matcher 的路由项,再各自进行路由。比如按域名进行分组路由。

func AndMatcher

func AndMatcher(m ...Matcher) Matcher

AndMatcher 按顺序符合每一个要求

前一个对象返回的实例将作为下一个对象的输入参数。

func AndMatcherFunc

func AndMatcherFunc(f ...func(*http.Request, *types.Context) bool) Matcher

AndMatcherFunc 需同时符合每一个要求

func NewHeaderVersion

func NewHeaderVersion(param, key string, errlog func(error), version ...string) Matcher

NewHeaderVersion 声明匹配报头 Accept 中版本号的 Matcher 实现

param 将版本号作为参数保存到上下文中时的名称,如果不需要保存参数,可以设置为空值; errlog 错误日志输出通道,如果为空则采用 log.Default; key 表示在 accept 报头中的表示版本号的参数名,如果为空则采用 version; version 版本的值,可能为空,表示匹配任意值;

func NewPathVersion

func NewPathVersion(param string, version ...string) Matcher

NewPathVersion 声明匹配路径中版本号的 Matcher 实例

param 将版本号作为参数保存到上下文中是的名称,如果不需要保存参数,可以设置为空值; version 版本的值,可以为空,表示匹配任意值;

NOTE: 会修改 [http.Request.URL.Path] 的值,去掉匹配的版本号路径部分,比如:

/v1/path.html

如果匹配 v1 版本,会修改为:

/path.html

func OrMatcher

func OrMatcher(m ...Matcher) Matcher

OrMatcher 仅需符合一个要求

func OrMatcherFunc

func OrMatcherFunc(f ...func(*http.Request, *types.Context) bool) Matcher

OrMatcherFunc 仅需符合一个要求

type MatcherFunc

type MatcherFunc func(*http.Request, *types.Context) bool

func (MatcherFunc) Match

func (f MatcherFunc) Match(r *http.Request, p *types.Context) bool

type Option

type Option func(*options)

func WithAllowedCORS

func WithAllowedCORS(maxAge int) Option

WithAllowedCORS 允许跨域请求

func WithAnyInterceptor

func WithAnyInterceptor(rule string) Option

WithAnyInterceptor 任意非空字符的拦截器

func WithCORS

func WithCORS(origin []string, allowHeaders []string, exposedHeaders []string, maxAge int, allowCredentials bool) Option

WithCORS 自定义跨域请求设置项

origin 对应 Origin 报头。如果包含了 *,那么其它的设置将不再启作用。 如果此值为空,表示不启用跨域的相关设置;

allowHeaders 对应 Access-Control-Allow-Headers 可以包含 *,表示可以是任意值,其它值将不再启作用;

exposedHeaders 对应 Access-Control-Expose-Headers;

maxAge 对应 Access-Control-Max-Age 有以下几种取值:

  • 0 不输出该报头;
  • -1 表示禁用;
  • 其它 >= -1 的值正常输出数值;

allowCredentials 对应 Access-Control-Allow-Credentials;

func WithDenyCORS

func WithDenyCORS() Option

WithDenyCORS 禁用跨域请求

func WithDigitInterceptor

func WithDigitInterceptor(rule string) Option

WithDigitInterceptor 任意数字字符的拦截器

func WithInterceptor

func WithInterceptor(f InterceptorFunc, rule ...string) Option

WithInterceptor 针对带参数类型路由的拦截处理

在解析诸如 /authors/{id:\\d+} 带参数的路由项时, 用户可以通过拦截并自定义对参数部分 {id:\\d+} 的解析, 从而不需要走正则表达式的那一套解析流程,可以在一定程度上增强性能。

一旦正则表达式被拦截,则节点类型也将不再是正则表达式, 其处理优先级会比正则表达式类型高。 在某些情况下,可能会造成处理结果不相同。比如:

/authors/{id:\\d+}     // 1
/authors/{id:[0-9]+}   // 2

以上两条记录是相同的,但因为表达式不同,也能正常添加, 处理流程,会按添加顺序优先比对第一条,所以第二条是永远无法匹配的。 但是如果你此时添加了 (InterceptorDigit, "[0-9]+"), 使第二个记录的优先级提升,会使第一条永远无法匹配到数据。

可多次调用,表示同时指定了多个。

func WithLock

func WithLock(l bool) Option

WithLock 是否加锁

在调用 Router.Handle 等添加路由时,有可能会改变整个路由树的结构, 如果需要频繁在运行时添加和删除路由项,那么应当添加此选项。

func WithLogRecovery

func WithLogRecovery(status int, l *log.Logger) Option

WithLogRecovery 将错误信息输出到日志

status 表示向客户端输出的状态码; l 为输出的日志;

func WithRecovery

func WithRecovery(f RecoverFunc) Option

WithRecovery 用于指定路由 panic 之后的处理方法

如果多次指定,则最后一次启作用。

func WithSLogRecovery

func WithSLogRecovery(status int, l *slog.Logger) Option

WithSLogRecovery 将错误信息输出到日志

status 表示向客户端输出的状态码; l 为输出的日志;

func WithStatusRecovery

func WithStatusRecovery(status int) Option

WithStatusRecovery 仅向客户端输出 status 状态码

func WithTrace

func WithTrace[T any](v T) Option

WithTrace 指定用于处理 TRACE 请求的方法

T 的类型应该同 NewRouter 中的类型参数 T,否则会 panic。

NOTE: Trace 提供了一种简单的 TRACE 处理方式。

func WithURLDomain

func WithURLDomain(prefix string) Option

WithURLDomain 为 Router.URL 生成的地址带上域名

func WithWordInterceptor

func WithWordInterceptor(rule string) Option

WithWordInterceptor 任意英文单词的拦截器

func WithWriteRecovery

func WithWriteRecovery(status int, out io.Writer) Option

WithWriteRecovery 向 io.Writer 输出错误信息

status 表示向客户端输出的状态码; out 表示输出通道,比如 os.Stderr 等;

type Prefix

type Prefix[T any] struct {
	// contains filtered or unexported fields
}

Prefix 操纵统一前缀的路由

func (*Prefix[T]) Any

func (p *Prefix[T]) Any(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Clean

func (p *Prefix[T]) Clean()

Clean 清除所有以 Prefix.Pattern 开头的路由项

当指定多个相同的 Prefix 时,调用其中的一个 Prefix.Clean 也将会清除其它的:

r := NewRouter(...)
p1 := r.Prefix("prefix")
p2 := r.Prefix("prefix")
p2.Clean() 将同时清除 p1 的内容,因为有相同的前缀。

func (*Prefix[T]) Delete

func (p *Prefix[T]) Delete(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Get

func (p *Prefix[T]) Get(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Handle

func (p *Prefix[T]) Handle(pattern string, h T, m []types.Middleware[T], methods ...string) *Prefix[T]

func (*Prefix[T]) Patch

func (p *Prefix[T]) Patch(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Pattern

func (p *Prefix[T]) Pattern() string

Pattern 当前对象的路径

func (*Prefix[T]) Post

func (p *Prefix[T]) Post(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Prefix

func (p *Prefix[T]) Prefix(prefix string, m ...types.Middleware[T]) *Prefix[T]

Prefix 在现有 Prefix 的基础上声明一个新的 Prefix 实例

m 中间件函数,按顺序调用可参考 Router.Use 的说明;

func (*Prefix[T]) Put

func (p *Prefix[T]) Put(pattern string, h T, m ...types.Middleware[T]) *Prefix[T]

func (*Prefix[T]) Remove

func (p *Prefix[T]) Remove(pattern string, methods ...string)

Remove 删除指定匹配模式的路由项

func (*Prefix[T]) Resource

func (p *Prefix[T]) Resource(pattern string, m ...types.Middleware[T]) *Resource[T]

Resource 创建一个资源路由项

pattern 资源地址; m 中间件函数,按顺序调用可参考 Router.Use 的说明;

func (*Prefix[T]) Router

func (p *Prefix[T]) Router() *Router[T]

Router 返回与当前关联的 Router 实例

func (*Prefix[T]) URL

func (p *Prefix[T]) URL(strict bool, pattern string, params map[string]string) (string, error)

URL 根据参数生成地址

type RecoverFunc

type RecoverFunc = func(http.ResponseWriter, any)

type Resource

type Resource[T any] struct {
	// contains filtered or unexported fields
}

Resource 以资源地址为对象的路由

func (*Resource[T]) Any

func (r *Resource[T]) Any(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Clean

func (r *Resource[T]) Clean()

Clean 清除当前资源的所有路由项

func (*Resource[T]) Delete

func (r *Resource[T]) Delete(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Get

func (r *Resource[T]) Get(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Handle

func (r *Resource[T]) Handle(h T, m []types.Middleware[T], methods ...string) *Resource[T]

func (*Resource[T]) Patch

func (r *Resource[T]) Patch(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Pattern

func (r *Resource[T]) Pattern() string

Pattern 当前对象的路径

func (*Resource[T]) Post

func (r *Resource[T]) Post(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Put

func (r *Resource[T]) Put(h T, m ...types.Middleware[T]) *Resource[T]

func (*Resource[T]) Remove

func (r *Resource[T]) Remove(methods ...string)

Remove 删除指定匹配模式的路由项

func (*Resource[T]) Router

func (r *Resource[T]) Router() *Router[T]

Router 返回与当前资源关联的 Router 实例

func (*Resource[T]) URL

func (r *Resource[T]) URL(strict bool, params map[string]string) (string, error)

URL 根据参数构建一条 URL

params 匹配路由参数中的同名参数,或是不存在路由参数,比如普通的字符串路由项, 该参数不启作用;

res, := m.Resource("/posts/{id}")
res.URL(map[string]string{"id": "1"}, "") // /posts/1

res, := m.Resource("/posts/{id}/{path}")
res.URL(map[string]string{"id": "1","path":"author/profile"}) // /posts/1/author/profile

type Router

type Router[T any] struct {
	// contains filtered or unexported fields
}

Router 可自定义处理函数类型的路由

router := NewRouter[http.Handler](...)
router.Get("/abc/h1", h1).
    Post("/abc/h2", h2).
    Handle("/api/{version:\\d+}",h3, http.MethodGet, http.MethodPost) // 只匹配 GET 和 POST
http.ListenAndServe(router)

func NewRouter

func NewRouter[T any](
	name string,
	call CallFunc[T],
	notFound T,
	methodNotAllowedBuilder, optionsBuilder types.BuildNodeHandler[T],
	o ...Option,
) *Router[T]

NewRouter 声明路由

name 路由名称,可以为空; methodNotAllowedBuilder 和 optionsBuilder 可以自定义 405 和 OPTIONS 请求的处理方式; o 用于指定一些可选的参数;

T 表示用户用于处理路由项的方法。

func (*Router[T]) Any

func (r *Router[T]) Any(pattern string, h T, m ...types.Middleware[T]) *Router[T]

Any 添加一条包含 AnyMethods 中请求方法的路由

func (*Router[T]) Clean

func (r *Router[T]) Clean()

Clean 清除当前路由组的所有路由项

func (*Router[T]) Delete

func (r *Router[T]) Delete(pattern string, h T, m ...types.Middleware[T]) *Router[T]

func (*Router[T]) Get

func (r *Router[T]) Get(pattern string, h T, m ...types.Middleware[T]) *Router[T]

Get 相当于 Router.Handle(pattern, h, http.MethodGet) 的简易写法

h 不应该主动调用 WriteHeader,否则会导致 HEAD 请求获取不到 Content-Length 报头。

func (*Router[T]) Handle

func (r *Router[T]) Handle(pattern string, h T, m []types.Middleware[T], methods ...string) *Router[T]

Handle 添加一条路由数据

pattern 为路由匹配模式,可以是正则匹配也可以是字符串匹配, 若语法不正确,则直接 panic,可以通过 CheckSyntax 检测语法的有效性,其它接口也相同; m 为应用于当前路由项的中间件; methods 该路由项对应的请求方法,如果未指定值,则采用 AnyMethods 返回的方法;

func (*Router[T]) Name

func (r *Router[T]) Name() string

Name 路由名称

func (*Router[T]) Patch

func (r *Router[T]) Patch(pattern string, h T, m ...types.Middleware[T]) *Router[T]

func (*Router[T]) Post

func (r *Router[T]) Post(pattern string, h T, m ...types.Middleware[T]) *Router[T]

func (*Router[T]) Prefix

func (r *Router[T]) Prefix(prefix string, m ...types.Middleware[T]) *Prefix[T]

Prefix 声明一个 Prefix 实例

prefix 路由前缀字符串,可以为空; m 中间件函数,按顺序调用可参考 Router.Use 的说明;

func (*Router[T]) Put

func (r *Router[T]) Put(pattern string, h T, m ...types.Middleware[T]) *Router[T]

func (*Router[T]) Remove

func (r *Router[T]) Remove(pattern string, methods ...string)

Remove 移除指定的路由项

当未指定 methods 时,将删除所有 method 匹配的项。 指定错误的 methods 值,将自动忽略该值。

func (*Router[T]) Resource

func (r *Router[T]) Resource(pattern string, m ...types.Middleware[T]) *Resource[T]

Resource 创建一个资源路由项

pattern 资源地址; m 中间件函数,按顺序调用,会继承 r 的中间件并按在 m 之前;

func (*Router[T]) Routes

func (r *Router[T]) Routes() map[string][]string

Routes 返回当前路由组的路由项

键名为请求地址,键值为对应的请求方法。

func (*Router[T]) ServeHTTP

func (r *Router[T]) ServeHTTP(w http.ResponseWriter, req *http.Request)

func (*Router[T]) URL

func (r *Router[T]) URL(strict bool, pattern string, params map[string]string) (string, error)

URL 根据参数生成地址

strict 是否检查路由是否真实存在以及参数是否符合要求; pattern 为路由项的定义内容; params 为路由项中的参数,键名为参数名,键值为参数值。

func (*Router[T]) Use

func (r *Router[T]) Use(m ...types.Middleware[T])

Use 使用中间件

对于中间件的使用,除了此方法,还可以 Router.PrefixRouter.ResourcePrefix.Prefix 以及添加路由项时指定。调用顺序如下:

NOTE: 对于 404 路由项的只有通过 Router.Use 应用的中间件有效。

Directories

Path Synopsis
examples
ctx
Package ctx 自定义路由
Package ctx 自定义路由
std
Package std 兼容标准库的路由
Package std 兼容标准库的路由
Package header 定义了 HTTP 请求中的报头
Package header 定义了 HTTP 请求中的报头
internal
syntax
Package syntax 负责处理路由语法
Package syntax 负责处理路由语法
tree
Package tree 提供了以树形结构保存路由项的相关操作
Package tree 提供了以树形结构保存路由项的相关操作
Package routertest 提供针对路由的测试用例
Package routertest 提供针对路由的测试用例
Package types 类型的前置声明
Package types 类型的前置声明

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL