HttpRouter
ServeMux的一个缺陷是:无法使用变量实现URL模式匹配。而HttpRouter则可以。
HttpRouter是一个高性能、可扩展的第三方HTTP路由包。HttpRouter包弥补了net/http包中默认路由不足的问题。
下面用一个例子认识一下HttpRouter这个强大的HTTP路由包。
打开命令行终端,输入如下命令即可完成HttpRouter安装:
go get -u github.com/julienschmidt/httprouter
HttpRouter的使用方法如下:
首先使用httprouter…New()函数生成了一个Router路由
对象,然后使用GET()方法注册一个适配/路径的Index函数,最后将Router对象作为参数传给ListenAndServe()函数即可启动HTTP服务。
import (
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
/*
使用HttpRouter包
*/
func main() {
router := httprouter.New()
router.GET("/", Index)
log.Fatal(http.ListenAndServe(":8080", router))
}
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Write([]byte("Index"))
}
HttpRouter包为常用的HTTP方法提供了快捷的使用方式:
func (r *Router) GET(path string, handle Handle) {
r.Handle(http.MethodGet, path, handle)
}
func (r *Router) HEAD(path string, handle Handle) {
r.Handle(http.MethodHead, path, handle)
}
func (r *Router) OPTIONS(path string, handle Handle) {
r.Handle(http.MethodOptions, path, handle)
}
// POST is a shortcut for router.Handle(http.MethodPost, path, handle)
func (r *Router) POST(path string, handle Handle) {
r.Handle(http.MethodPost, path, handle)
}
func (r *Router) PUT(path string, handle Handle) {
r.Handle(http.MethodPut, path, handle)
}
func (r *Router) PATCH(path string, handle Handle) {
r.Handle(http.MethodPatch, path, handle)
}
func (r *Router) DELETE(path string, handle Handle) {
r.Handle(http.MethodDelete, path, handle)
}
HttpRouter包中对URL使用两种匹配模式:
- 形如/user/name的精确匹配
- 形如/user/*name的匹配所有的模式
import (
"github.com/julienschmidt/httprouter"
"net/http"
)
/*
httprouter的匹配模式
*/
func main() {
router := httprouter.New()
router.GET("/default", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Write([]byte("default get"))
})
router.POST("/default", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Write([]byte("default post"))
})
//精确匹配
router.GET("/user/name", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Write([]byte("user name:" + p.ByName("name")))
})
//匹配所有
router.POST("/user/*name", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Write([]byte("user name:" + p.ByName("name")))
})
http.ListenAndServe(":8080", router)
}
Handler包可以处理不同的二级域名。它先根据域名获取对应的Handler路由,然后调用处理(分发机制)。
import (
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func main() {
userRouter := httprouter.New()
userRouter.GET("/", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Write([]byte("sub1"))
})
dataRouter := httprouter.New()
dataRouter.GET("/", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Write([]byte("sub2"))
})
//分别处理不同的二级域名
hs := make(HostMap)
hs["sub1.localhost:8080"] = userRouter
hs["sub2.localhost:8080"] = dataRouter
log.Fatal(http.ListenAndServe(":8080", hs))
}
type HostMap map[string]http.Handler
func (hs HostMap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//先根据域名获取对应的Handler路由,然后调用处理
if handler := hs[r.Host]; handler != nil {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Forbidden", 403)
}
}
HttpRouter包提供了很方便的静态文件服务。如果要把一个目录托管在服务器上以供访问,则只需要调用ServeFiles()方法。该方法的定义如下:
func (r *Router)ServeFiles(path string,root http.FileSystem)
在使用ServeFiles()方法时需要注意:第1个参数路径必须是*/filepath形式,第2个参数为文件目录。示例代码如下。
import (
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func main() {
router := httprouter.New()
//访问静态文件
router.ServeFiles("/static/*filepath", http.Dir("./files"))
log.Fatal(http.ListenAndServe(":8080", router))
}
HttpRouter包允许使用者设置PanicHandler,以处理在HTTP请求中发生的panic异常。Handler包通过PanicHandler处理异常的示例代码如下。
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func main() {
router := httprouter.New()
router.GET("/", Index1)
//捕获异常
router.PanicHandler = func(w http.ResponseWriter, r *http.Request, v interface{}) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "error:%s", v)
}
log.Fatal(http.ListenAndServe(":8080", router))
}
func Index1(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
panic("error")
}
HttpRouter包的Router结构体还有其他配置,比如是否通过重定向、是否监测当前请求的方法被允许等配置。Router结构体里的代码如下:
type Router struct {
//是否通过重定向给路径自动去掉斜杠(/)
//例如:如果请求了/foo/,但路由只存在/foo
//则对于GET请求,客户端被重定向到/foo,HTTP状态代码为301
RedirectTrailingSlash bool
//是否通过重定向自动修复路径,比如双斜杠(//)被自动修复为单斜杠(/)
RedirectFixedPath bool
//是否监测当前请求的方法被允许
HandleMethodNotAllowed bool
//是否自动答复OPTION请求
HandleOPTIONS bool
//404错误的默认处理
NotFound http.Handler
//不被允许的方法的默认处理
MethodNotAllowed http.Handler
//异常容易处理
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
}