在入口函数main()
的default
分支中,对路由进行了注册,现在分析下。
##main()中路由注册相关代码
源码:
httpRouter := httptreemux.New()
// Blog and pages as http
server.InitializeBlog(httpRouter)
server.InitializePages(httpRouter)
// Admin as http
server.InitializeAdmin(httpRouter)
// Start http server
log.Println("Starting server without HTTPS support. Please enable HTTPS in " + filenames.ConfigFilename + " to improve security.")
log.Println("Starting http server on port " + httpPort + "...")
err := http.ListenAndServe(httpPort, httpRouter)
if err != nil {
log.Fatal("Error: Couldn't start the HTTP server:", err)
}
###httptreemux.New()
func New() *TreeMux {
root := &node{path: "/"}
return &TreeMux{root: root,
NotFoundHandler: http.NotFound,
MethodNotAllowedHandler: MethodNotAllowedHandler,
HeadCanUseGet: true,
RedirectTrailingSlash: true,
RedirectCleanPath: true,
RedirectBehavior: Redirect301,
RedirectMethodBehavior: make(map[string]RedirectBehavior),
PathSource: RequestURI,
}
}
可以看到他返回了一个实例化的结构体TreeMux
,这个结构体注册了根路径/
,此外还包含一些默认的handle,如http.NotFound
:
// NotFound replies to the request with an HTTP 404 not found error.
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
###server.InitializeBlog(httpRouter)
func InitializeBlog(router *httptreemux.TreeMux) {
// For index
router.GET("/", indexHandler)
router.GET("/:slug/", postHandler)
router.GET("/page/:number/", indexHandler)
// For author
router.GET("/author/:slug/", authorHandler)
router.GET("/author/:slug/:function/", authorHandler)
router.GET("/author/:slug/:function/:number/", authorHandler)
// For tag
router.GET("/tag/:slug/", tagHandler)
router.GET("/tag/:slug/:function/", tagHandler)
router.GET("/tag/:slug/:function/:number/", tagHandler)
// For serving asset files
router.GET("/assets/*filepath", assetsHandler)
router.GET("/images/*filepath", imagesHandler)
router.GET("/content/images/*filepath", imagesHandler) // This is here to keep compatibility with Ghost
router.GET("/public/*filepath", publicHandler)
}
这里使用了route.GET()
函数注册路由,我们看下router.GET("/", indexHandler)
:
// Syntactic sugar for Handle("GET", path, handler)
func (t *TreeMux) GET(path string, handler HandlerFunc) {
t.Handle("GET", path, handler)
}
t.Handle("GET", path, handler)
:
func (t *TreeMux) Handle(method, path string, handler HandlerFunc) {
if path[0] != '/' {
panic(fmt.Sprintf("Path %s must start with slash", path))
}
addSlash := false
if len(path) > 1 && path[len(path)-1] == '/' && t.RedirectTrailingSlash {
addSlash = true
path = path[:len(path)-1]
}
//将path注册到root子节点中去,并返回子节点的引用
node := t.root.addPath(path[1:], nil)
if addSlash {
node.addSlash = true
}
//最终注册路由
node.setHandler(method, handler)
}
node.setHandler(method, handler):
func (n *node) setHandler(verb string, handler HandlerFunc) {
if n.leafHandler == nil {
n.leafHandler = make(map[string]HandlerFunc)
}
_, ok := n.leafHandler[verb]
if ok {
panic(fmt.Sprintf("%s already handles %s", n.path, verb))
}
n.leafHandler[verb] = handler
}
剩下的路由注册server.InitializeBlog(httpRouter)
,server.InitializePages(httpRouter)
,server.InitializeAdmin(httpRouter)
都大同小异。
这里只是介绍了路由注册的整体流程,其中最复杂的地方是func (t *TreeMux) Handle(method, path string, handler HandlerFunc)
中的node := t.root.addPath(path[1:], nil)
函数,不过源码有详细的注释,有兴趣的自行研究。