Go 自定义Url路由器
2019-06-24
张子阳
分类: Go 语言
Go Web编程比较不方便的一点就是URL路由,也叫多路复用器(ServerMux),实现的比较“粗放”。简单来说,就是匹配的规则太广太简单,没有提供通配符、占位符的机制。这篇文章将重写一个简单的URL路由器,使其支持通配符。
为了保留一部分的使用习惯,我们首先了解一下go提供的默认路由器(DefaultServerMux)的规则是什么样的。如下表所示:
模式 | 说明 |
---|---|
/ | 匹配所有:/、/a、/a/b、/a/b/c ... |
/a | 仅匹配/a |
/a/ | 当没有配置/a时,匹配/a/和/a,并且/a的Path会被重写为/a/;当配置了/a时,则分别进行匹配。 除此以外,还匹配:/a/b、/a/b/c ... |
这个匹配规范显然有点宽了,当以“/”收尾时,模式就相当于一个“前缀”了。因此,我们可以自定两个通配符:“+”和“*”。
- + : 仅匹配一个目录或者是资源(即路径末尾)。
- * : 当它位于路径中间时,它匹配一个或者多个目录;当它位于路径末尾时,它匹配零个或者多个资源。
还是举一些例子看得更清楚一些:
模式 | 说明 |
---|---|
/ | 仅匹配 / |
/a | 仅匹配/a |
/a/+ | 匹配 /a/b、/a/c ...,不匹配 /a/b/c、/a/b/c/d |
/a/+/c | 匹配 /a/b/c、/a/d/c |
/a/*/c | 匹配 /a/b/c、/a/d/e/c、/a/b/d/f/c ... |
/a/* | 匹配 /a/、/a/b、/a/d/、/a/b/d/f ... |
实现这个路由器只需要实现Handler接口,它只定义了一个ServeHTTP方法:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
下面是实现的主要代码:
package router
import (
"net/http"
"strings"
)
// MyRouter 路由
type myRouter struct {
// 保存用户的 pattern 和 对应的handlerFunc
handlers map[string]http.HandlerFunc
// 保存用户pattern,主要是为了按顺序,因为map没有顺序
patterns []string
}
// DefaultRouter 路由
var DefaultRouter = &myRouter{handlers: map[string]http.HandlerFunc{}, patterns: []string{}}
// ServeHTTP
func (x *myRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
for _, k := range x.patterns {
if match(k, path) {
x.handlers[k](w, r)
return
}
}
// 全都不匹配则404
http.NotFound(w, r)
}
func (x *myRouter) HandleFunc(pattern string, handler http.HandlerFunc) {
if !checkPattern(pattern) {
panic("pattern格式有误,检查是否存在下面模式:/+/*、/*/+、/*/*,可使用/*代替")
}
if _, ok := x.handlers[pattern]; !ok {
// 如果不存在,在list中加一个
x.patterns = append(x.patterns, pattern)
} else {
panic("URL模式 " + pattern + " 重复注册了!")
}
x.handlers[pattern] = handler
}
// 验证模式和路径是否匹配
func match(pattern string, path string) bool {
...
}
// 验证模式,不能出现 /+/* 或者 /*/+ ,或者 /*/*
// 因为它们都等于 /*/
func checkPattern(pattern string) bool {
...
}
使用方法如下:
package main
import (
"fmt"
"net/http"
"tracefact/router"
)
func main() {
fmt.Println("server start.")
r := router.DefaultRouter
// 仅匹配 /
r.HandleFunc("/", getHandler("/"))
// 仅匹配 /a
r.HandleFunc("/a", getHandler("/a"))
// 仅匹配 /b/ 和 /b
r.HandleFunc("/b/", getHandler("/b/"))
// 匹配 /a/x/y/b、匹配 /a/x/b ...
r.HandleFunc("/a/*/b", getHandler("/a/*/b"))
// 匹配 /c/、/c/x、/c/x/y ...
r.HandleFunc("/c/*", getHandler("/c/*"))
http.ListenAndServe(":8081", r)
}
func getHandler(pattern string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body := r.URL.Path + " : " + pattern
fmt.Println(body)
fmt.Fprintln(w, body)
}
}
完整代码可以查看这里:https://github.com/tracefact/router
感谢阅读,希望这篇文章能给你带来帮助!