API 请求体
把 govalid 接入 net/http(或任何 framework)的处理器中。校验在 JSON 解码之后、业务逻辑之前运行。
配合 net/http
go
package main
import (
"encoding/json"
"errors"
"net/http"
"github.com/wuhan005/govalid"
"golang.org/x/text/language"
)
type CreatePost struct {
Title string `valid:"required;minlen:3;maxlen:200" label:"标题"`
Body string `valid:"required;minlen:10" label:"正文"`
Tags []string `valid:"maxlen:5" label:"标签"`
Author string `valid:"required;username" label:"作者"`
Category string `valid:"list:tech,life,news" label:"分类"`
}
func (p *CreatePost) Validate() error {
if len(p.Body) > 100 && len(p.Tags) == 0 {
return errors.New("长文章必须至少包含一个标签")
}
return nil
}
func createPost(w http.ResponseWriter, r *http.Request) {
var body CreatePost
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
locale := negotiate(r)
if errs, ok := govalid.Check(&body, locale); !ok {
respondValidationErrors(w, errs)
return
}
// ……业务逻辑……
w.WriteHeader(http.StatusCreated)
}
func negotiate(r *http.Request) language.Tag {
matcher := language.NewMatcher([]language.Tag{
language.Chinese, language.English,
})
accept, _, _ := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
tag, _, _ := matcher.Match(accept...)
return tag
}
func respondValidationErrors(w http.ResponseWriter, errs []*govalid.ErrContext) {
type item struct {
Field string `json:"field"`
Label string `json:"label"`
Message string `json:"message"`
}
out := make([]item, 0, len(errs))
for _, e := range errs {
out = append(out, item{
Field: e.FieldName,
Label: e.FieldLabel,
Message: e.Error(),
})
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnprocessableEntity)
_ = json.NewEncoder(w).Encode(map[string]any{"errors": out})
}示例响应
json
{
"errors": [
{ "field": "Title", "label": "标题", "message": "标题长度应大于3" },
{ "field": "Author", "label": "作者", "message": "作者的第一个字符必须为字母" }
]
}模式要点
- 在解码之后才调用
Check。校验标签解决不了 JSON 格式错误。 - 把协商好的
language.Tag传给Check,让响应使用调用方的 locale。 - 校验失败时优先用 HTTP
422 Unprocessable Entity,而不是400 Bad Request——前端通常需要区分"我发的就是垃圾"和"你的数据 需要修一下"。