今天这篇文章我们将会为我们之前编写的HTTP
服务器加上复杂路由的功能以及对路由进行分组管理。在之前的文章《深入学习用 Go 编写HTTP服务器》中详细地讲了使用net/http
进行路由注册、监听网络连接、处理请求、安全关停服务的实现方法,使用起来非常方便。但是net/http
有一点做的不是非常好的是,它没有提供类似URL
片段解析、路由参数绑定这样的复杂路由功能。好在在Go
社区中有一个非常流行的gorilla/mux
包,它提供了对复杂路由功能的支持。在今天这篇文章中我们将探究如何用gorilla/mux
包来创建具有命名参数、GET/POST
处理、分组前缀、限制访问域名的路由。
安装gorilla/mux
包
我们在之前写的HTTP
服务的代码根目录,使用go get命令从GitHub安装软件包,如下所示:
go get github.com/gorilla/mux
复制代码
在《深入学习用 Go 编写HTTP服务器》中我们介绍过路由注册、匹配和最后处理函数的调用都是由ServeMux
(服务复用器)来完成的,而且我们还自己定义了复用器用以替换默认的DefaultServeMux
。同样的gorilla/mux
包也是为我们提供了一个复用器。这个复用器拥有很多功能用以提升编写Web
应用的效率,而且与标准的http.ServeMux
兼容。
使用gorilla/mux
包
创建路由器
可以像下面这样创建一个路由器
router := mux.NewRouter()
复制代码
会返回一个mux.Router
实例,mux.Router
将传入的请求与已注册路由列表进行匹配,并为与URL
或其他条件匹配的路由调用处理程序。主要特点是:
- 可以根据URL主机,路径,路径前缀,
Header
头、查询值,HTTP
方法进行路由匹配,或是使用自定义匹配器。 - URL主机,路径和查询值可以是带有可选正则表达式的变量。
- 路由可以被用作子路由,只有父路由匹配后才会尝试匹配子路由。这对于定义路由组非常有用,路由组可以共享主机、路径前缀、或者其他常见的属性。
- 它实现了http.Handler接口,因此与标准的
http.ServeMux
完全兼容。
注册路由处理程序
我们将之前程序里自定义的服务复用器替换成上面创建好mux.Router
,并为其注册路由处理器。
type helloHandler struct{}
func (*helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func WelcomeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome!")
}
func main() {
router := mux.NewRouter()
router.Handle("/", &helloHandler{})
router.HandleFunc("/welcome", WelcomeHandler)
......
}
复制代码
之前我们是用http.Hanle
,http.HandleFunc
注册处理程序的,这里直接换成router.Hanle
和router.HanleFunc
即可,很方便。
定义带命名参数的路由
使用mux.Router
的最大优势是可以从请求URL
中提取分段,然后作为命名参数传入路由处理程序供使用。
接下来为我们的程序注册一个路由处理器,让服务器能够处理URL
为/names/Gorge/countries/NewZealand
的请求:
router.HandleFunc("/names/{name}/countries/{country}", func(writer http.ResponseWriter, request *http.Request) {
......
})
复制代码
接下来在处理函数中使用mux.Vars()
函数从这些URL
分段中获取数据。该函数以http.Request
为参数并返回一个URL
分段名为键,提取的数据为值的字典。
func(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
name := vars["name"]
country := vars["country"]
fmt.Fprintf(writer, "This guy named %s, was coming from %s .", name, country)
})
复制代码
让服务器使用我们创建的路由器
这个设置很简单,如果没有自定义http.Server
对象,使用http.ListenAndServe(":8000", router)
,使用自己定义的`http.Server 对象时则是:
server := &http.Server{
Addr: ":8080",
Handler: router,
}
复制代码
这个和我们把自定义的服务复用器传递给http.Server
没有任何区别。
改造完后我们之前写的HTTP
服务器就可以根据具体的 URL 动态地构造响应。关键字回复http02
可获得完整的源代码
其他gorilla/mux
路由器的常用功能
设置路由的HTTP方法
限制路由处理器只处理指定的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
方案限制为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)
复制代码
使用gorilla/mux
改进我们的HTTP
服务器
接下来我们使用gorilla/mux
对我们之前写的HTTP
服务器做一下改进,之前我们所有程序都放在了main.go
中,现在我们的程序还很小,所以我们先不把项目目录规划的太复杂,先通过文件做下简单的职责划分,新建两个文件router.go
和handler.go
分别用来存放路由注册的逻辑和路由对应的处理器函数,两个文件的示例内容如下。
handler.go
:
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
)
type HelloHandler struct{}
func (*HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func ShowVisitorInfo(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
name := vars["name"]
country := vars["country"]
fmt.Fprintf(writer, "This guy named %s, was coming from %s .", name, country)
}
复制代码
router.go
:
package main
import (
"github.com/gorilla/mux"
)
func RegisterRoutes(r *mux.Router) {
indexRouter := r.PathPrefix("/index").Subrouter()
indexRouter.Handle("/", &HelloHandler{})
userRouter := r.PathPrefix("/user").Subrouter()
userRouter.HandleFunc("/names/{name}/countries/{country}", ShowVisitorInfo)
}
复制代码
在router.go
中我们将路由分为index
和user
两组,在两个路由组上分别定义路由。将这部分封装在一个导出函数RegisterRoutes
供调用。这样即使以后路由注册的程序要放到单独的目录里也可以供外部调用。
整理完后我们的main.go
中就会变的很简洁:
func main() {
//mux := http.NewServeMux()
//mux.Handle("/", &helloHandler{})
muxRouter := mux.NewRouter()
RegisterRoutes(muxRouter)
server := &http.Server{
Addr: ":8080",
Handler: muxRouter,
}
......
err := server.ListenAndServe()
......
}
复制代码
关注公众号回复go-http-02
获取本文中完整的示例代码。
前文回顾:深入学习用 Go 编写HTTP服务器
喜欢我的文章,帮忙转发点赞,如在实践过程中遇到什么问题可在下方给我留言。
有疑问加站长微信联系(非本文作者)