## 使用闭包进行状态处理 将状态传递给处理程序通常很棘手。有两种方法:通过闭包传递状态,这有助于提高单个处理程序的灵活性,或使用结构体进行传递。 我们将使用结构控制器来存储接口,并使用由外部函数修改的单个处理程序创建两个路由。 ### 实践 1. 建立 controller.go: ``` package controllers // Controller 传递状态给处理函数 type Controller struct { storage Storage } func New(storage Storage) *Controller { return &Controller{ storage: storage, } } type Payload struct { Value string `json:"value"` } ``` 2. 建立 storage.go: ``` package controllers // Storage 接口支持存取单个值 type Storage interface { Get() string Put(string) } // MemStorage 实现了 Storage接口 type MemStorage struct { value string } func (m *MemStorage) Get() string { return m.value } func (m *MemStorage) Put(s string) { m.value = s } ``` 3. 建立 post.go: ``` package controllers import ( "encoding/json" "net/http" ) // SetValue 修改Controller的存储内容 func (c *Controller) SetValue(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusInternalServerError) return } value := r.FormValue("value") c.storage.Put(value) w.WriteHeader(http.StatusOK) p := Payload{Value: value} if payload, err := json.Marshal(p); err == nil { w.Write(payload) } } ``` 4. 建立 get.go: ``` package controllers import ( "encoding/json" "net/http" ) // GetValue是一个封装HandlerFunc的闭包,如果UseDefault为true,则值始终为“default”,否则它将是存储在storage中的任何内容 func (c *Controller) GetValue(UseDefault bool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) return } value := "default" if !UseDefault { value = c.storage.Get() } p := Payload{Value: value} w.WriteHeader(http.StatusOK) if payload, err := json.Marshal(p); err == nil { w.Write(payload) } } } ``` 5. 建立 main.go: ``` package main import ( "fmt" "net/http" "github.com/agtorre/go-cookbook/chapter7/controllers" ) func main() { storage := controllers.MemStorage{} c := controllers.New(&storage) http.HandleFunc("/get", c.GetValue(false)) http.HandleFunc("/get/default", c.GetValue(true)) http.HandleFunc("/set", c.SetValue) fmt.Println("Listening on port :3333") err := http.ListenAndServe(":3333", nil) panic(err) } ``` 6. 运行: ``` go run main.go ``` 这会输出 ``` Listening on port :3333 ``` 进行请求测试: ``` $curl "http://localhost:3333/set -X POST -d "value=value" {"value":"value"} $curl "http://localhost:3333/get -X GET {"value":"value"} $curl "http://localhost:3333/get/default -X GET {"value":"default"} ``` ### 说明 这种策略有效,因为Go允许函数传递。我们可以用类似的方法传入数据库连接、日志记录等。在示例中,我们插入了一个storage接口,所有请求的处理方法都可以使用其方法和属性。 GetValue方法没有传递http.HandlerFunc签名,而是直接返回它,我们通过这种方式来注入状态。在main.go中,我们定义了两个路由,其中UseDefault设置为false,另一个路由设置为true。这可以在定义跨越多个路由的函数时使用,也可以在使用处理程序感觉过于繁琐的结构时使用。 * * * * 学识浅薄,错误在所难免。欢迎在群中就本书提出修改意见,以飨后来者,长风拜谢。 Golang中国(211938256) beego实战(258969317) Go实践(386056972)