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
感谢阅读,希望这篇文章能给你带来帮助!