Gorilla学习笔记
简介
Gorilla是一个Web工具箱,包括以下包:
包 | 说明 |
gorilla/context | 存储请求变量 |
gorilla/mux | URL路由和分发器 |
gorilla/reverse | 支持基于正则式的路由 |
gorilla/rpc | 实现RPC over HTTP |
gorilla/schema | 将值转换为结构 |
gorilla/securecookie | 编解码验证过的、可选加密的Cookie |
gorilla/sessions | 保存Cookie和Session,支持自定义的Session后端 |
gorilla/websocket | 实现WebSocket协议 |
mux
路由
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import "github.com/gorilla/mux" func main() { // 新建一个请求路由器 r := mux.NewRouter() // 路由器可以限定匹配的域名 r.Host("blog.gmem.cc") r.Host("[a-z].cdn.gmem.cc") // 路由器可以限定URL前缀 r.PathPrefix("/media/") // 可以限定支持的HTTP方法 r.Methods("GET", "POST") // 限定HTTPS r.Schemes("https") // 限定请求头 r.Headers("X-Requested-With", "XMLHttpRequest") r.HeadersRegexp("Content-Type", "application/(text|json)") // 限定请求参数 r.Queries("key", "value") // 自定义匹配函数 r.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { return r.ProtoMajor == 0 }) // 支持链式调用 r.Queries("", "").Queries("", "") // 根据URL来路由 // 支持{name} 或 {name:pattern}形式的路径变量 // 路径变量中可以包含正则式,例如:{id:[0-9]+}表示将匹配[0-9]+的一个不跨越/的片断捕获为路径变量id // 正则式中可以使用非捕获分组:不支持{sort:(asc|desc)};支持/{sort:(?:asc|desc)}(非捕获) r.HandleFunc("/media/{id}", MediaHandler) // 指定由上述路由器处理的URL模式 http.Handle("/", r) http.ListenAndServe(":8080", nil) } func MediaHandler(writer http.ResponseWriter, request *http.Request) { // 路径变量可以这样读取 vars := mux.Vars(request) id := vars["id"] } |
子路由
你可以针对每个处理器设置匹配规则:
1 2 |
r.HandleFunc("/user", UserHandler).Host("blog.gmem.cc").Schemes("http") r.HandleFunc("/media", MediaHandler).Host("blog.gmem.cc").Schemes("http") |
但是这样一次次指定很麻烦,可以使用子路由来共享匹配规则:
1 2 3 |
s := r.Host("blog.gmem.cc").Schemes("http").Subrouter() s.HandleFunc("/user",UserHandler) s.HandleFunc("/media",MediaHandler) |
你可以为子路由指定一个路径前缀:
1 2 3 4 5 |
s := r.PathPrefix("/admin").Subrouter() // /admin s.HandleFunc("/", AdminHomeHandler) // /admin/role/1 s.HandleFunc("/role/{id}", RoleHandler) |
路径前缀具有通配的性质:
1 2 |
// 匹配所有 /static/*请求 r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) |
中间件
Mux支持为路由器添加中间件,如果匹配路由或者子路由,则按照添加的顺序执行中间件。中间件通常会针对请求进行处理,然后传递给另一个中间件,或者传递给最终的请求处理器。
中间件是一个函数,其签名为:
1 |
type MiddlewareFunc func(http.Handler) http.Handler |
下面是一个简单的记录请求的中间件:
1 2 3 4 5 6 7 |
func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println(r.RequestURI) // 调用下一个中间件 next.ServeHTTP(w, r) }) } |
要使用中间件,调用路由器的方法:
1 |
r.Use(LoggingMiddleware) |
context
路由器可以从请求中抽取变量,并允许处理器函数访问这些变量,Context为此提供了支持:
1 2 3 4 5 6 7 8 9 10 11 |
import "github.com/gorilla/context" func main() { // 存储键值 context.Set(request, key, value) // 获取键值 value := context.Get(request, key) value, ok := context.GetOk(request, key) // 在请求处理的最后,要清空上下文 context.Clear(r) } |
从1.7开始Go标准库提供了Context支持,你可以使用此标准API:
1 2 3 4 5 6 7 8 9 10 11 |
func SessionMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 设值 ctx := context.WithValue(context.Background(), "token", r.URL.Query()["token"][0]) req := r.WithContext(ctx) // 装饰为绑定了Context的请求,传递给下一个中间件 next.ServeHTTP(w, req) }) } // 取值 req.Context().Value("token") |
schema
此包能够把请求参数转换为结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import "github.com/gorilla/schema" type Person struct { Name string `schema:"name"` // 支持schema标签 Phone string `schema:"-"` // - 表示此字段不进行绑定 } // 请求参数映射 values := map[string][]string{ "Name": {"Alex"}, "Phone": {"13399999999"}, } person := new(Person) decoder := schema.NewDecoder() decoder.Decode(person, values) |
支持的结构字段类型包括:bool、浮点数、整数、string、struct、前述类型的切片、前述类型的指针
websocket
此包提供对WebSocket协议的支持:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, // 启用压缩,试验特性 EnableCompression: true, } func handler(w http.ResponseWriter, r *http.Request) { // 升级为WebSocket协议 conn, _ := upgrader.Upgrade(w, r, nil) for { // 回响服务 // 消息类型可以是websocket.BinaryMessage、websocket.TextMessage messageType, p, _ := conn.ReadMessage() conn.WriteMessage(messageType, p) // 也可以使用io.WriteCloser、io.Reader来收发消息 messageType, reader, _ := conn.NextReader() writer, _ := conn.NextWriter(messageType) io.Copy(writer, reader) // 一直读取到EOF writer.Close() } } |
注意:每个连接仅支持一个并发的Reader、Writer。你需要保证仅仅有一个Goroutine进行读写。
Leave a Reply