API リクエストボディ
govalid を net/http(あるいは任意のフレームワーク)の HTTP ハン ドラーに組み込みます。バリデーションは 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に渡し、レスポンスを 呼び出し側のロケールに揃えます。 - バリデーション失敗には
400 Bad Requestではなく422 Unprocessable Entityを返すと、フロントエンドが「ゴミを送った」 と「データの修正が必要」を区別しやすくなります。