Go语言基础(五)httprouter详解

httprouter 是一个高性能、可扩展的HTTP路由,可以作为golang默认路由net/http的替代。

关键词:httprouter

安装

1
go get -u  "github.com/julienschmidt/httprouter"

一个例子作为开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"log"
"net/http"

"github.com/julienschmidt/httprouter"
)

func HelloWorld(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Write([]byte("HelloWorld"))
}

func main() {
router := httprouter.New()
router.GET("/hi", HelloWorld)
log.Fatal(http.ListenAndServe(":80", router))
}

上面的代码中,HelloWorld是一个handle httprouter.Handle类型,需要传入三个参数,三个参数的作用以后说。该handle在main函数忠被注册到/hi路径上。运行代码会得到一下效果。

HTTP Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func (r *Router) GET(path string, handle Handle) {
r.Handle("GET", path, handle)
}

func (r *Router) HEAD(path string, handle Handle) {
r.Handle("HEAD", path, handle)
}

func (r *Router) OPTIONS(path string, handle Handle) {
r.Handle("OPTIONS", path, handle)
}

func (r *Router) POST(path string, handle Handle) {
r.Handle("POST", path, handle)
}

func (r *Router) PUT(path string, handle Handle) {
r.Handle("PUT", path, handle)
}

func (r *Router) PATCH(path string, handle Handle) {
r.Handle("PATCH", path, handle)
}

func (r *Router) DELETE(path string, handle Handle) {
r.Handle("DELETE", path, handle)
}

路由匹配

net/http的路由匹配

1
2
3
4
5
6
7
8
9
10
11
//	/api,可以访问到
// /api/,不可以
http.HandleFunc("/api",func(w http.ResponseWriter,r *http.Request){
fmt.Println("/api")
})

// /api,可以
// /api/,也可以
http.HandleFunc("/api/",func(w http.ResponseWriter,r *http.Request){
fmt.Println("/api")
})

httprouter的路由匹配

两者路由命名捕获方式:(是路由命名不是路由参数)

  • :name的捕获方式是匹配内容直到下一个斜线或者路径的结尾

    1
    2
    3
    4
    5
    6
    7
    8
    Path: /blog/:category/:post  
    router.GET("/blog/:category/:post", Hello) //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的方式是从指定位置开始(包含前缀"/")匹配到结尾

    1
    2
    3
    4
    5
    6
    7
    8
    Path: /files/*filepath
    router.GET("/files/*filepath", Hello) //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

获取路由命名的参数

1
2
3
4
5
6
func HelloWorld(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
name := ps.ByName("who") //获取who对应的参数
name := ps[0].Value //直接通过Value检索
w.Write([]byte("HelloWorld"))
}
router.GET("/hi/:who", HelloWorld)

httprouter重定向

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

1
2
3
4
5
6
7
8
9
10
11
12
func New() *Router {
return &Router{
//是否启用自动重定向
RedirectTrailingSlash: true,
// 设置为true时回尝试修复路径, 第一个多余的路径会被删除. 之后, 路由器对已清理的路径进行不区分大小写的查找. 如果可以找到此路由的句柄,则路由器将重定向到正确的路径
RedirectFixedPath: true,
HandleMethodNotAllowed: true,

//如果启用,则路由器会自动回复OPTIONS请求
HandleOPTIONS: true,
}
}

下面有几种会重定向的情况

1
2
3
4
5
6
7
8
注册路径:/blog/:category/:post
请求URL路径:/blog/go/request-routers/

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

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

httprouter lookup

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

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

httprouter获取请求相关的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func HelloWorld(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
err := r.ParseForm() // 解析表单必须
if err != nil {
w.WriteHeader(http.StatusBadGateway)
}
fmt.Printf("Who: %v\n", ps.ByName("who"))
fmt.Printf("Method: %v\n", r.Method)
fmt.Printf("Host: %v\n", r.Host)
fmt.Printf("UserAgent: %v\n", r.UserAgent())
fmt.Printf("r.PostForm: %v\n", r.PostForm)
fmt.Printf("r.Form: %v\n", r.Form)
fmt.Println("========OK=======")
}

其中PostForm获取x-www-form-urlencoded发送的表单,Form获取明文发送如http://127.0.0.1/hi/xxx?user=xxx如user=xxx的信息。

http返回信息

1
2
3
4
5
6
7
8
9
func HelloWorld(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.WriteHeader(http.statusOK)
status, err := w.Write([]byte{})
if err := nil {
return
}
w.Header()
}

第一行返回http请求状态码

第二行返回Body,通常是返回一个json

第三行是返回Header,格式是map