服务计算web技术之原代码阅读与分析
先给出分析代码的链接:mux.go
路由Router
创建Router实例
type Router struct {
// Configurable Handler to be used when no route matches.
NotFoundHandler http.Handler
// Configurable Handler to be used when the request method does not match the route.
MethodNotAllowedHandler http.Handler
// Routes to be matched, in order.
routes []*Route
// Routes by name for URL building.
namedRoutes map[string]*Route
// If true, do not clear the request context after handling the request.
//
// Deprecated: No effect, since the context is stored on the request itself.
KeepContext bool
// Slice of middlewares to be called after a match is found
middlewares []middleware
// configuration shared with `Route`
routeConf
}
routes []*Route
代表router列表
routeConf
则是定义了一些路由当中的flag配置:
**useEncodedPath:**使用的编码路径
strictSlash::定义斜杠/行为
**skipClean:**清洗path的行为
type routeConf struct {
// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
useEncodedPath bool
// If true, when the path pattern is "/path/", accessing "/path" will
// redirect to the former and vice versa.
strictSlash bool
// If true, when the path pattern is "/path//to", accessing "/path//to"
// will not redirect
skipClean bool
// Manager for the variables from host and path.
regexp routeRegexpGroup
// List of matchers.
matchers []matcher
// The scheme used when building URLs.
buildScheme string
buildVarsFunc BuildVarsFunc
}
SeverHTTP
r.skipClean的判断是用于判断是否跳过对路径进行清洗(例如想去掉不需要的双下滑线:把“/fetch/http://xkcd.com/534/”清洗成“/fetch/http/xkcd.com/534/”),并将重定向至清洗后的链接。
然后是利用一个r.Match获取一个合适的匹配器RouteMatch。在经过一系列的正确性确认后,方法将调用返回的handle服务发送到的请求。
// ServeHTTP dispatches the handler registered in the matched route.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
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
}
}
var match RouteMatch
var handler http.Handler
if r.Match(req, &match) {
handler = match.Handler
req = requestWithVars(req, match.Vars)
req = requestWithRoute(req, match.Route)
}
if handler == nil && match.MatchErr == ErrMethodMismatch {
handler = methodNotAllowedHandler()
}
if handler == nil {
handler = http.NotFoundHandler()
}
handler.ServeHTTP(w, req)
}
然后再看看Match
Match代码,程序直接遍历所有route,试着找到一个能匹配当前请求的route。
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
for _, route := range r.routes {
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
}
}
if match.MatchErr == ErrMethodMismatch {
if r.MethodNotAllowedHandler != nil {
match.Handler = r.MethodNotAllowedHandler
return true
}
return false
}
// Closest match for a router (includes sub-routers)
if r.NotFoundHandler != nil {
match.Handler = r.NotFoundHandler
match.MatchErr = ErrNotFound
return true
}
match.MatchErr = ErrNotFound
return false
}
HandleFunc
HandleFunc是用于注册的函数,在r的routes表里添加新的一项,设置这一项的Path以及处理函数。
// HandleFunc registers a new route with a matcher for the URL path.
// See Route.Path() and Route.HandlerFunc().
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
*http.Request)) *Route {
return r.NewRoute().Path(path).HandlerFunc(f)
}
像这样定义Path之类的函数还有很多:
func (r *Route) Headers(pairs …string) *Route:增加头部筛选条件
func (r *Route) Host(tpl string) *Route:增加主机筛选条件
func (r *Route) Queries(pairs …string) *Route:增加Query参数的筛选条件
从源代码角度对比 DefaultServeMux 与 gorilla/mux
路由器的差别是由ServeHTTP造成的
gorilla/mux使用了一个第三方模块gorilla/context。当http请求到来时,mux.Router会选择合适的路由,并提取出一些参数信息,将这些参数信息与http.Request对象在gorilla/context中建立映射关系,上层处理函数根据http.Request对象到context中找到该http.Request所对应的参数信息。
DefaultServeMux的ServeHTTP函数:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
DefaultServeMux不支持正则路由。
只支持路径匹配,不支持按照Method,header,host等信息匹配,无法实现RESTful架构。