gorilla/mux程序包,是一个强大的url路由和调度器,它具有小巧但是稳定高效的特性。
不仅可以支持正则路由,还可以按照Method、header、host等信息匹配,可以从我们设定的路由表达式中提取出参数方便上层应用,而且完全兼容http.ServerMux。
Go 官方标准库 net/http
自带的 DefaultServeMux
底层实现,通过 DefaultServeMux
提供的路由处理器虽然简单易上手,但是存在很多不足,比如:
/user/:uid
这种泛类型匹配;为此,我们可以使用第三方库 gorilla/mux
提供的更加强大的路由处理器(mux
代表 HTTP request multiplexer
,即 HTTP 请求多路复用器),和 http.ServeMux
实现原理一样,gorilla/mux
提供的路由器实现类 mux.Router
也会匹配用户请求与系统注册的路由规则,然后将用户请求转发过去。
mux.Router
主要具备以下特性:
http.Handler
接口,所以和 http.ServeMux
完全兼容;安装程序包:
go get -u github.com/gorilla/mux
样例①:
基于Golang的简单web服务程序开发——CloudGo
主要的相关函数为NewServer函数与initRoutes函数:
// NewServer configures and returns a Server.
func NewServer() *negroni.Negroni {
formatter := render.New(render.Options{
Directory: "templates",
Extensions: []string{".html"},
IndentJSON: true,
})
n := negroni.Classic()
mx := mux.NewRouter()
initRoutes(mx, formatter)
n.UseHandler(mx)
return n
}
func initRoutes(mx *mux.Router, formatter *render.Render) {
webRoot := os.Getenv("WEBROOT")
if len(webRoot) == 0 {
if root, err := os.Getwd(); err != nil {
panic("Could not retrive working directory")
} else {
webRoot = root
//fmt.Println(root)
}
}
mx.HandleFunc("/api/test", apiTestHandler(formatter)).Methods("GET")
mx.HandleFunc("/", homeHandler(formatter)).Methods("GET")
mx.HandleFunc("/user", userHandler).Methods("POST")
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}
样例②:
package main
import (
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
)
func sayHelloWorld(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) // 设置响应状态码为 200
fmt.Fprintf(w, "Hello, World!") // 发送响应到客户端
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", sayHelloWorld)
log.Fatal(http.ListenAndServe(":8080", r))
}
在 main
函数中的第一行显式初始化了 mux.Router
作为路由器,然后在这个路由器中注册路由规则,最后将这个路由器传入 http.ListenAndServe
方法,整个调用过程和之前并无二致,因为我们前面说了,mux.Router
也实现了 Handler
接口。
运行这段代码,在浏览器访问 http://localhost:8080/hello
,即可渲染出如下结果:
Hello, World!
gorilla/mux源码可分为context、mux、regex、route四个部分,在CloudGo项目开发过程中,我主要使用了NewRouter、HandleFunc和PathPrefix这三个函数。下面对这三个函数进行分析:
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
// Parent route, if this is a subrouter.
parent parentRoute
// Routes to be matched, in order.
routes []*Route
// Routes by name for URL building.
namedRoutes map[string]*Route
// See Router.StrictSlash(). This defines the flag for new routes.
strictSlash bool
// See Router.SkipClean(). This defines the flag for new routes.
skipClean bool
// If true, do not clear the request context after handling the request.
// This has no effect when go1.7+ is used, since the context is stored
// on the request itself.
KeepContext bool
// see Router.UseEncodedPath(). This defines a flag for all routes.
useEncodedPath bool
}
调用NewRouter函数可用来实例化一个Router:
// NewRouter returns a new router instance.
func NewRouter() *Router {
return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
}
这里可以看见,它开辟了一个装Route指针的map,然后默认该Router的KeepContext
为false
,意思是在请求被处理完之后清除该请求的上下文。
// 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)
}
若只观察HandleFunc函数,会发现其代码只有几行,其主要功能是使用URL的匹配器注册新路由。
gorilla/mux的HandleFunc函数功能很强大,主要有以下功能:
限制路由处理器只处理指定的HTTP
方法的请求:
router.HandleFunc("/books/{title}", CreateBook).Methods("POST")
router.HandleFunc("/books/{title}", ReadBook).Methods("GET")
router.HandleFunc("/books/{title}", UpdateBook).Methods("PUT")
router.HandleFunc("/books/{title}", DeleteBook).Methods("DELETE")
上面的就是一组可以响应具体HTTP
方法的RESTful
风格的接口的路由。
限制路由处理器只处理访问指定域名加路由的请求:
router.HandleFunc("/books/{title}", BookHandler).Host("www.mybookstore.com")
将请求处理程序可响应的HTTP
方案限制为http
或者https
。
router.HandleFunc("/secure", SecureHandler).Schemes("https")
router.HandleFunc("/insecure", InsecureHandler).Schemes("http")
bookrouter := router.PathPrefix("/books").Subrouter()
bookrouter.HandleFunc("/", AllBooks)
bookrouter.HandleFunc("/{title}", GetBook)
// PathPrefix registers a new route with a matcher for the URL path prefix.
// See Route.PathPrefix().
func (r *Router) PathPrefix(tpl string) *Route {
return r.NewRoute().PathPrefix(tpl)
}
PathPrefix函数源码也只有几行,它的功能只是简单地增加URL的前缀,通常结合HandleFunc函数和Handler函数来使用。