## 使用閉包進行狀態處理
將狀態傳遞給處理程序通常很棘手。有兩種方法:通過閉包傳遞狀態,這有助于提高單個處理程序的靈活性,或使用結構體進行傳遞。
我們將使用結構控制器來存儲接口,并使用由外部函數修改的單個處理程序創建兩個路由。
### 實踐
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)
- 前言
- 第一章 I/O和文件系統
- 常見 I/O 接口
- 使用bytes和strings包
- 操作文件夾和文件
- 使用CSV格式化數據
- 操作臨時文件
- 使用 text/template和HTML/templates包
- 第二章 命令行工具
- 解析命令行flag標識
- 解析命令行參數
- 讀取和設置環境變量
- 操作TOML,YAML和JSON配置文件
- 操做Unix系統下的pipe管道
- 處理信號量
- ANSI命令行著色
- 第三章 數據類型轉換和解析
- 數據類型和接口轉換
- 使用math包和math/big包處理數字類型
- 貨幣轉換和float64注意事項
- 使用指針和SQL Null類型進行編碼和解碼
- 對Go數據編碼和解碼
- Go中的結構體標簽和反射
- 通過閉包實現集合操作
- 第四章 錯誤處理
- 錯誤接口
- 使用第三方errors包
- 使用log包記錄錯誤
- 結構化日志記錄
- 使用context包進行日志記錄
- 使用包級全局變量
- 處理恐慌
- 第五章 數據存儲
- 使用database/sql包操作MySQL
- 執行數據庫事務接口
- SQL的連接池速率限制和超時
- 操作Redis
- 操作MongoDB
- 創建存儲接口以實現數據可移植性
- 第六章 Web客戶端和APIs
- 使用http.Client
- 調用REST API
- 并發操作客戶端請求
- 使用OAuth2
- 實現OAuth2令牌存儲接口
- 封裝http請求客戶端
- 理解GRPC的使用
- 第七章 網絡服務
- 處理Web請求
- 使用閉包進行狀態處理
- 請求參數驗證
- 內容渲染
- 使用中間件
- 構建反向代理
- 將GRPC導出為JSON API
- 第八章 測試
- 使用標準庫進行模擬
- 使用Mockgen包
- 使用表驅動測試
- 使用第三方測試工具
- 模糊測試
- 行為驅動測試
- 第九章 并發和并行
- 第十章 分布式系統
- 第十一章 響應式編程和數據流
- 第十二章 無服務器編程
- 第十三章 性能改進