源码托管地址 Gorilla, 官方网站 gorillatoolkit.
Gorilla 在 Github 上是一个开源组, 值得敬佩的是, 这个小组开发出了多个实用并且独立并解耦的 package. 详情查看官网和源码, 这里做一个简单的介绍.
不要被 schema 这个名字迷惑, 这其实是一个 form 提交数据到 struct 实例的转换器. 非常实用的一个功能. 使用也非常简单. 但是如果您查看源码发现没有引入 net/http
包, 这就是解耦. 因为 http.Request.Form
其实就是一个 map[string][]string
类型.
代码说话
<!-- lang: cpp -->
package main
import (
"fmt"
"github.com/gorilla/schema"
)
type Person struct {
Name string
Phone string
}
func main() {
// 模拟一个 Form 数据
values := map[string][]string{
"Name": {"John"},
"Phone": {"999-999-999"},
}
person := new(Person)
decoder := schema.NewDecoder()
decoder.Decode(person, values) // 从Form数据 SetTo *Person
fmt.Printf("%#v\n", person)
}
是不是很简单, 其代码实现也很简单. 而且 Schema 还提供了注册转换函数
<!-- lang: cpp -->
type Converter func(string) reflect.Value
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter)
比如我们常见的 time.Time 类型, 因为 layout 的多样性, 可以根据我们的应用写独立的转换函数
<!-- lang: cpp -->
func StringToTime(s string) reflect.Value {
t, _ := time.Parse("2006-01-02", s)
return reflect.ValueOf(t)
}
type Person struct {
Name string
Phone string
}
func main() {
// 模拟一个 Form 数据
values := map[string][]string{
"Name": {"John"},
"Phone": {"999-999-999"},
"Day": {"2013-02-01"},
}
person := new(Person)
decoder := schema.NewDecoder()
decoder.RegisterConverter(time.Now(), StringToTime)
decoder.Decode(person, values) // 从Form数据 SetTo *Person
fmt.Printf("%#v\n", person)
}
实用, 简单, 解耦
context 把变量值关联 *http.Request
存储的工具. 看源码
data = make(map[*http.Request]map[interface{}]interface{})
就是一个 map.如果您熟悉jQuery,那这个工具和 jQuery.fn.data 有异曲同工之妙.
代码说话, 直接看 test 吧
当然一定要记住 Clear(r)
. 因为每一个 *http.Request 都是新的. 害怕忘记, 不要紧context
提供了 ClearHandler
, 可以对http.Handler
进行包装,代码很简单
<!-- lang: cpp -->
func ClearHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer Clear(r)
h.ServeHTTP(w, r)
})
}
http.Handler 结束后自动Clear
.
反之如果 Request 结束了. 服务器端内存中还保留着 data[*http.Request]
, 这是不合理的.
这个实现其实非常简单, 而且是并发安全的. 很明显某些场合下, 这会让共享参数变的简单. 有意思的地方是你会如何用她, 会产生什么样的效果.
securecookie 通过 HMAC 算法, 实现了对数据进行安全可逆编码(加密).
名字里面虽然带有cookie, 其实并没有绑定必须使用 cookie. 只是表示加密后的数据用于 cookie 也没有问题.
session 综合了 context
,securecookie
完成了会话数据存储支持. 内建提供 cookies
和filesystem
. 当然也预留了Store
接口
<!-- lang: cpp -->
type Store interface {
Get(r *http.Request, name string) (*Session, error)
New(r *http.Request, name string) (*Session, error)
Save(r *http.Request, w http.ResponseWriter, s *Session) error
}
使用者完全可以自己实现一个. 别忘了, 前面介绍的 ClearHandler
的作用.
mux 是一个 Request 路由器分派器( 大家习惯上讲路由 router ). mux doc 非常的强大.
代码说话
<!-- lang: cpp -->
r := mux.NewRouter()
r.Host("{subdomain}.domain.com").
Path("/articles/{category}/{id:[0-9]+}").
HandlerFunc(ArticleHandler).
Name("article")
// url.String() will be "http://news.domain.com/articles/technology/42"
url, err := r.Get("article").URL("subdomain", "news","category", "technology","id", "42")
功能不完全列举
r.PathPrefix("/products/")
匹配所有 “/products/” 开头的 Requestr.Methods("get")
匹配 GET
请求r.PathPrefix("/products").Subrouter()
, 这样与前缀路由(或者其他,比如Method)配合, 可以更细致的控制s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
,解析路径中的变量r.Host("{subdomain}.domain.com")
, 是的,子域名匹配什么的就这么简单