Go Web:HttpRouter路由

HttpRouter是一个轻量级但却很是高效的multiplexer。手册:
https://godoc.org/github.com/julienschmidt/httprouter
https://github.com/julienschmidt/httprouterhtml

用法示例

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "net/http"
    "log"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)

    log.Fatal(http.ListenAndServe(":8080", router))
}

首先执行:git

go get github.com/julienschmidt/httprouter

而后再启动web服务:github

go run xxx.go

和http包的ServeMux用法其实很相似。上面定义了两个httprouter中的handle,相似于http包中的http.HandlerFunc类型,具体的对应关系后文会解释,只要认为它是handler,是处理对应请求的就好了。而后使用New()方法建立了实例,并使用GET()方法为两个模式注册了对应的handler。web

须要注意的是,第二个模式"/hello/:name",它能够用来作命名匹配,相似于正则表达式的命名捕获分组。后面会详细解释用法。正则表达式

httprouter用法说明

Variables
func CleanPath(p string) string
type Handle
type Param
type Params
    func ParamsFromContext(ctx context.Context) Params
    func (ps Params) ByName(name string) string
type Router
    func New() *Router
    func (r *Router) DELETE(path string, handle Handle)
    func (r *Router) GET(path string, handle Handle)
    func (r *Router) HEAD(path string, handle Handle)
    func (r *Router) Handle(method, path string, handle Handle)
    func (r *Router) Handler(method, path string, handler http.Handler)
    func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc)
    func (r *Router) Lookup(method, path string) (Handle, Params, bool)
    func (r *Router) OPTIONS(path string, handle Handle)
    func (r *Router) PATCH(path string, handle Handle)
    func (r *Router) POST(path string, handle Handle)
    func (r *Router) PUT(path string, handle Handle)
    func (r *Router) ServeFiles(path string, root http.FileSystem)
    func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

type Handle

httprouter中的Handle相似于http.HandlerFunc,只不过它支持第三个参数Params。函数

type Handle func(http.ResponseWriter, *http.Request, Params)
    Handle is a function that can be registered to a route to handle HTTP
    requests. Like http.HandlerFunc, but has a third parameter for the values of
    wildcards (variables).

例如前面示例中的Index()和Hello()都是Handle类型的实例。post

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

注册handler

httprouter.Router类型相似于http包中的ServeMux,它实现了http.Handler接口,因此它是一个http.Handler。它能够将请求分配给注册好的handler。code

type Router struct {}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

除此以外,Router提供了很多方法,用来指示如何为路径注册handler。router

func (r *Router) Handle(method, path string, handle Handle)
func (r *Router) Handler(method, path string, handler http.Handler)
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc)

httprouter.Handle()用于为路径注册指定的Handle,而httprouter.Handle对应于http.HandlerFunc,因此是直接将Handle类型的函数绑定到指定路径上。同时,它还能够指定http方法:GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS。htm

这些方法还有对应的各自缩写:

func (r *Router) DELETE(path string, handle Handle)
func (r *Router) GET(path string, handle Handle)
func (r *Router) HEAD(path string, handle Handle)
func (r *Router) OPTIONS(path string, handle Handle)
func (r *Router) PATCH(path string, handle Handle)
func (r *Router) POST(path string, handle Handle)
func (r *Router) PUT(path string, handle Handle)

例如,Get()等价于route.Handle("GET", path, handle)。

例如上面的示例中,为两个路径注册了各自的httprouter.Handle函数。

router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)

Handler()方法是直接为指定http方法和路径注册http.Handler;HandlerFunc()方法则是直接为指定http方法和路径注册http.HandlerFunc。

Param相关

type Param struct {
    Key   string
    Value string
}
Param is a single URL parameter, consisting of a key and a value.

type Params []Param
Params is a Param-slice, as returned by the router. The slice is ordered, the first URL parameter is also the first slice value. It is therefore safe to read values by the index.

func (ps Params) ByName(name string) string
ByName returns the value of the first Param which key matches the given name. If no matching Param is found, an empty string is returned.

Param类型是key/value型的结构,每一个分组捕获到的值都会保存为此类型。正如前面的示例中:

router.GET("/hello/:name", Hello)

这里的:name就是key,当请求的URL路径为/hello/abc,则key对应的value为abc。也就是说保存了一个Param实例:

Param{
    Key: "name",
    Value: "abc",
}

更多的匹配用法稍后解释。

Params是Param的slice。也就是说,每一个分组捕获到的key/value都存放在这个slice中。

ByName(str)方法能够根据Param的Key检索已经保存在slice中的Param的Value。正如示例中:

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

router.GET("/hello/:name", Hello)

这里ByName("name")将检索保存在slice中,Key="name"的Param,且返回这个Param中的Value。

因为Params是slice结构,除了ByName()方法能够检索key/value,经过slice的方法也能够直接检索:

ps[0].Key
ps[0].Value

路径匹配规则

httprouter要为路径注册handler的适合,路径能够进行命名捕获。有两种命名捕获的方式:

Syntax    Type
:name     named parameter
*name     catch-all parameter

其中:name的捕获方式是匹配内容直到下一个斜线或者路径的结尾。例如要为以下路径注册handler:

Path: /blog/:category/:post

当请求路径为:

/blog/go/request-routers            match: category="go", post="request-routers"
/blog/go/request-routers/           no match, but the router would redirect
/blog/go/                           no match
/blog/go/request-routers/comments   no match

*name的捕获方式是从指定位置开始(包含前缀"/")匹配到结尾:

Path: /files/*filepath

/files/                             match: filepath="/"
/files/LICENSE                      match: filepath="/LICENSE"
/files/templates/article.html       match: filepath="/templates/article.html"
/files                              no match, but the router would redirect

再解释下何时会进行重定向。在Router类型中,第一个字段控制尾随斜线的重定向操做:

type Router struct {
    RedirectTrailingSlash bool
    ...
}

若是请求的URL路径包含或者不包含尾随斜线时,但在注册的路径上包含了或没有包含"/"的目标上定义了handler,可是会进行301重定向。简单地说,无论URL是否带尾随斜线,只要注册路径不存在,但在去掉尾随斜线或加上尾随斜线的路径上定义了handler,就会自动重定向。

例如注册路径为/foo,请求路径为/foo/,会重定向。

下面还有几种会重定向的状况:

注册路径:/blog/:category/:post
请求URL路径:/blog/go/request-routers/

注册路径:/blog/:category
请求URL路径:/blog/go

注册路径:/files/*filepath
请求URL路径:/files

Lookup()

func (r *Router) Lookup(method, path string) (Handle, Params, bool)

Lookup根据method+path检索对应的Handle,以及Params,并能够经过第三个返回值判断是否会进行重定向。

相关文章
相关标签/搜索