Categorygithub.com/issue9/mux/v3
modulepackage
3.2.0
Repository: https://github.com/issue9/mux.git
Documentation: pkg.go.dev

# README

mux Go Go version Go Report Card license codecov PkgGoDev

mux 是一个实现了 http.Handler 的中间件,为用户提供了以下功能:

  1. 路由参数;
  2. 支持正则表达式作为路由项匹配方式;
  3. 丰富的 OPTIONS 请求处理方式;
  4. 自动生成 HEAD 请求内容;
  5. 根据路由生成地址;
  6. 自定义附加的路由匹配项,比如限定域名,或是限定版本号等;
  7. 任意风格的路由,比如 discuz 这种不以 / 作为分隔符的;
m := mux.New(false, false, false, nil, nil).
    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
    Options("/users/1", "GET").     // OPTIONS /user/1 手动指定该路由项的 OPTIONS 请求方法返回内容

// 统一前缀路径的路由
p := m.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", m)

正则表达式

路由中支持以正则表达式的方式进行匹配,表达式以大括号包含,内部以冒号分隔, 前半部分为变量的名称,后半部分为变量可匹配类型的正则表达式。比如:

/posts/{id:\\d+} // 将被转换成 /posts/(?P<id>\\d+)
/posts/{:\\d+}   // 将被转换成 /posts/\\d+

命名参数

若路由字符串中,所有的正则表达式冒号之后的内容是特定的内容,或是无内容, 则会被转换成命名参数,因为有专门的验证方法,性能会比较正则稍微好上一些。

 /posts/{id}.html                  // 匹配 /posts/1.html
 /posts-{id}-{page}.html           // 匹配 /posts-1-10.html
 /posts/{id:digit}.html            // 匹配 /posts/1.html
 /posts/{path}.html                // 匹配 /posts/2020/11/11/title.html

目前支持以下作为命名参数的内容约束:

  • digit 限定为数字字符,相当于正则的 [0-9];
  • word 相当于正则的 [a-zA-Z0-9];
  • any 表示匹配任意非空内容;
  • "" 为空表示任意内容,包括非内容;

用户也可以自行添加新的约束符。具体可参考 https://pkg.go.dev/github.com/issue9/mux/v3/interceptor

在路由字符串中若是以命名参数结尾的,则表示可以匹配之后的任意字符。

/blog/assets/{path}       // 可以匹配 /blog/assets/2020/11/11/file.ext 等格式
/blog/{tags:\\w+}/{path}
/blog/assets{path}

路径匹配规则

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

  1. 普通路由优先于正则路由;
  2. 正则路由优先于命名路由;

比如:

/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

Matcher

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

// server
m := mux.Default()
host := m.Matcher(mux.NewHosts("*.example.com"))
host.Get("/path", h)
http.ListenAndServe(":8080", m)

// client
r := http.NewRequest(http.MethodGet, "https://abc.example.com/path", nil)
r.Do() // 正确访问 h 的返回内容

r := http.NewRequest(http.MethodGet, "/path", nil)
r.Do() // 无法访问 h 的返回内容

路由参数

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

params := Params(r)

id, err := params.Int("id")
 // 或是
id := params.MustInt("id", 0) // 0 表示在无法获取 id 参数的默认值

OPTIONS

默认情况下,用户无须显示地实现它,系统会自动实现。 当然用户也可以使用 *.Options() 函数指定特定的数据; 或是直接使用 *.Handle() 指定一个自定义的实现方式。

如果不需要的话,也可以在 New() 中将 disableOptions 设置为 true。 显示设定 OPTIONS,不受 disableOptions 的影响。

m := mux.Default()
m.Get("/posts/{id}", nil)     // 默认情况下, OPTIONS 的报头为 GET, OPTIONS
m.Options("/posts/{id}", "*") // 强制改成 *
m.Delete("/posts/{id}", nil)  // OPTIONS 依然为 *

m.Remove("/posts/{id}", http.MethodOptions)    // 在当前路由上禁用 OPTIONS
m.Handle("/posts/{id}", h, http.MethodOptions) // 显示指定一个处理函数 h

HEAD

默认情况下,用户无须显示地实现 HEAD 请求, 系统会为每一个 GET 请求自动实现一个对应的 HEAD 请求, 当然也与 OPTIONS 一样,你也可以自通过 mux.Handle() 自己实现 HEAD 请求。

性能

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

中间件

mux 本身就是一个实现了 http.Handler 接口的中间件, 所有实现官方接口 http.Handler 的都可以附加到 mux 上作为中间件使用。

middleware 提供了诸如按域名过滤等常用的中间件功能。

版权

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

# Packages

Package interceptor 针对带参数类型路由的拦截处理 在解析诸如 /authors/{id:\\d+} 带参数的路由项时, 用户可以通过拦截并自定义对参数部分 {id:\\d+} 的解析, 从而不需要走正则表达式的那一套解析流程,可以在一定程度上增强性能。 一旦正则表达式被拦截,则节点类型也将不再是正则表达式, 其处理优先级会比正则表达式类型高。 在某些情况下,可能会造成处理结果不相同。比如: /authors/{id:\\d+} // 1 /authors/{id:[0-9]+} // 2 以上两条记录是相同的,但因为表达式不同,也能正常添加, 处理流程,会按添加顺序优先比对第一条,所以第二条是永远无法匹配的。 但是如果你此时添加了 Register(MatchDigit, "[0-9]+"), 将第二个记录的优先级作为提升,以后的匹配都是优先第二条, 造成第一条永远无法匹配到数据。 除非是改造旧有的项目,否则建议自定义一些约束符来处理。比如: /authors/{id:digit} 用户只要注册一个 Register(MatchDigit, "digit") 即可拦截针对 digit 的项, 且不会影响正则表达式的处理。 interceptor 也是本着这样的原则,添加了以下拦截器: - digit 数字; - word 单词,即 [a-zA-Z0-9]+; - any 任意非空内容;.
Package params 获取和转换路由中的参数信息.

# Functions

Default New 的默认参数版本要.
IsWell 语法格式是否正确 如果出错,则会返回具体的错误信息。.
Methods 返回所有支持的请求方法.
New 声明一个新的 Mux disableOptions 是否禁用自动生成 OPTIONS 功能; disableHead 是否禁用根据 Get 请求自动生成 HEAD 请求; skipCleanPath 是否不对访问路径作处理,比如 "//api" ==> "/api"; notFound 404 页面的处理方式,为 nil 时会调用默认的方式进行处理; methodNotAllowed 405 页面的处理方式,为 nil 时会调用默认的方式进行处理;.
NewHosts 声明新的 Hosts 实例.
Params 获取路由的参数集合 NOTE: 详细情况可参考 params.Get.

# Variables

ErrNameExists 存在相同名称 当为一个路由项命名时,若存在相同名称的,则返回此错误信息。.

# Structs

Hosts 限定域名的匹配工具.
Mux 提供了强大的路由匹配功能 可以对路径按正则或是请求方法进行匹配。用法如下: m := mux.New() m.Get("/abc/h1", h1).
Prefix 可以将具有统一前缀的路由项集中在一起操作 example: p := srv.Prefix("/api") p.Get("/users") // 相当于 srv.Get("/api/users") p.Get("/user/1") // 相当于 srv.Get("/api/user/1").
Resource 以资源地址为对象的路由配置 r, _ := srv.Resource("/api/users/{id}") r.Get(h) // 相当于 srv.Get("/api/users/{id}") r.Post(h) // 相当于 srv.Post("/api/users/{id}") url := r.URL(map[string]string{"id":5}) // 获得 /api/users/5.
Router 用于描述 Mux.All 返回的参数.

# Interfaces

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

# Type aliases

MatcherFunc 用于转一个 Match(http.Request) bool 转换成 Matcher 接口.