前言
Mux是一个Go语言实现的Dispatcher,用来处理各种http请求,完全兼容http.ServerMux,相比之下,有几个有点是原生ServerMux不具备的:
- 支持正则路由
- 支持按照Method,header,host等信息匹配.
这篇主要看一下Mux的底层源码。
源码解析
这里忽略用法,具体用法可以参考官方文档,我们从ServeHTTP方法开始看,这个ServeHTTP是原生http包的接口(http.Handler)的方法,说明我们的Router其实就是Handler接口的实现类:
if !r.skipClean {
path := req.URL.Path
if r.useEncodedPath {
path = req.URL.EscapedPath()
}
// Clean path to canonical form and redirect.
if p := cleanPath(path); p != path {
url := *req.URL
url.Path = p
p = url.String()
w.Header().Set("Location", p)
w.WriteHeader(http.StatusMovedPermanently)
return
}
}
我们先看开头部分,这里主要是在清理url,举个栗子:有个url是/path//to,如果这个Router设置了清理url,就会变成:/path/to。这里官方注解里也给了一些建议,如果你的url是:/fetch/http://xkcd.com/534/,清理之后会变成:/fetch/http/xkcd.com/534。
继续往下看:
var match RouteMatch
var handler http.Handler
//从router中找出符合要求的handler
if r.Match(req, &match) {
handler = match.Handler
req = setVars(req, match.Vars)
req = setCurrentRoute(req, match.Route)
}
//如果找到的不符合要求
if handler == nil && match.MatchErr == ErrMethodMismatch {
handler = methodNotAllowedHandler()
}
//如果找不到,执行没有找到handler的处理方式
if handler == nil {
handler = http.NotFoundHandler()
}
if !r.KeepContext {
defer contextClear(req)
}
handler.ServeHTTP(w, req)
这里主要是从Router中找出一个符合的Handler,再去执行Handler的ServeHTTP。
我们看一下Match方法的前半部分:
for _, route := range r.routes {
//调用route的Match方法
if route.Match(req, match) {
// Build middleware chain if no error was found
if match.MatchErr == nil {
for i := len(r.middlewares) - 1; i >= 0; i-- {
match.Handler = r.middlewares[i].Middleware(match.Handler)
}
}
return true
}
}
循环Router中的所有Route,找到符合条件的。每个Route里都有一个matcher数组,matcher是一个接口,实现类有以下几种:
用来匹配:请求头、方法(GET/POST)等。这个matcher是在我们调用Router的添加筛选条件(Header,Method等)时加入的。
我们以方法匹配为例简单看一下实现:
// methodMatcher matches the request against HTTP methods.
type methodMatcher []string
func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
return matchInArray(m, r.Method)
}
// matchInArray returns true if the given string value is in the array.
func matchInArray(arr []string, value string) bool {
for _, v := range arr {
if v == value {
return true
}
}
return false
}
方法匹配(匹配GET/POST等)的方式就是在支持的方法中寻找本次请求的Request的Method,其他规则不细说了。
至此,Mux的匹配流程就结束了。