apaas.dev
7 November 2022
SEO Title
本文提供了有关使用普通 Go HTTP 处理程序编写 JSON REST API 的随机提示。
- 使用匿名类型解析 JSON
- 使用 http.MaxBytesReader 限制请求长度
- 使用 map[string]interface{} 生成 JSON
- 使用 MarshalJSON 自定义 JSON 输出
- 使用中间件处理错误
- 使用结构对处理程序进行分组
- 使用分段/编码
使用匿名类型解析 JSON
而不是声明全局类型:
type ArticleRequest struct {
Name string
}
func handler(w http.ResponseWriter, req *http.Request) {
article := new(ArticleRequest)
if err := json.NewDecoder(req.Body).Decode(article); err != nil {
panic(err)
}
}
您可以改为声明匿名内联类型:
func handler(w http.ResponseWriter, req *http.Request) {
var in struct {
Name string
}
if err := json.NewDecoder(req.Body).Decode(&in); err != nil {
panic(err)
}
}
优点:
- 没有悬空类型。
- 处理程序彼此分离。
- 类型在使用它们的地方声明。
使用 http.MaxBytesReader 限制请求长度
默认情况下,Go 不对传入请求的长度施加任何限制。您应该使用 MaxBytesReader 自己处理。
func handler(w http.ResponseWriter, req *http.Request) {
req.Body = http.MaxBytesReader(w, req.Body, 1<<20) // 1MB
}
要快速计算字节数,请使用以下技巧:
3 << 10
- 3 kilobytes.3 << 20
- 3 megabytes.3 << 30
- 3 gigabytes.
使用 map[string]interface{} 生成 JSON
通常不值得声明一个结构来生成 JSON 响应。使用地图更容易,而且速度稍慢。一些框架甚至为 map[string]interface{} 提供了一个简短的类型别名,例如 gin.H 或 treemux.H。
type H map[string]interface{}
func handler(w http.ResponseWriter, req *http.Request) {
if err := json.NewEncoder(w).Encode(H{
"articles": articles,
"count": count,
}); err != nil {
panic(err)
}
}
使用 MarshalJSON 自定义 JSON 输出
您可以编写以下代码来自定义 JSON 输出,但它失败并出现致命错误:堆栈溢出错误。
type User struct{
Name string
}
func (u *User) MarshalJSON() ([]byte, error) {
if u.Name == "" {
u.Name = "anonymous"
}
// This call causes infinite recursion.
return json.Marshal(u)
}
您可以通过使用原始类型作为基础声明一个新类型来修复它:
type jsonUser User
func (u *User) MarshalJSON() ([]byte, error) {
if u.Name == "" {
u.Name = "anonymous"
}
return json.Marshal((*jsonUser)(u))
}
使用中间件处理错误
而不是编写这样的代码:
func handler(w http.ResponseWriter, req *http.Request) {
if processRequest(req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(H{}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
您可以创建一个为您处理错误的中间件:
func handler(w http.ResponseWriter, req *http.Request) error {
if processRequest(req); err != nil {
return err
}
if err := json.NewEncoder(w).Encode(H{}); err != nil {
return err
}
return nil
}
func errorHandler(next func(w http.ResponseWriter, req *http.Request) error) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if err := next(w, req); err != nil {
// You should change status code depending on the error.
http.Error(w, err.Error(), http.StatusBadRequest)
}
})
}
或者您可以使用开箱即用的提供此类功能的 echo 或 treemux。
使用结构对处理程序进行分组
而不是使用普通函数:
const rowLimit = 100
const rateLimit = 10
func showUser(w http.ResponseWriter, req *http.Request) {}
func listUsers(w http.ResponseWriter, req *http.Request) {}
func delUser(w http.ResponseWriter, req *http.Request) {}
最好定义一个结构并将所有相关状态存储在那里:
type UserHandler struct{
rowLimit int
rateLimit int
}
func (h *UserHandler) Show(w http.ResponseWriter, req *http.Request) {}
func (h *UserHandler) List(w http.ResponseWriter, req *http.Request) {}
func (h *UserHandler) Del(w http.ResponseWriter, req *http.Request) {}
使用分段/编码
segmentio/encoding 是 encoding/json 的直接替代品,比原始包快 2-3 倍。开始使用它所需要做的就是更新导入路径:
-import "encoding/json"
+import "github.com/segmentio/encoding/json"
它还提供了直接与 []byte 一起工作的较低级别的 API,并且效率更高:
func Append(b []byte, x interface{}, flags AppendFlags) ([]byte, error)
func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error)
- 登录 发表评论