govalidator
Go言語のための包括的なバリデーションとサニタイゼーションライブラリ
govalidatorは、文字列、数値、スライス、構造体に対する包括的なバリデータとサニタイザーを提供するGo言語向けのパッケージです。JavaScript の validator.js をベースに設計されており、豊富な組み込みバリデータとカスタムバリデータのサポートを提供します。
主な特徴
- 豊富な組み込みバリデータ: 120以上の組み込みバリデーション関数
- 構造体タグベースのバリデーション: タグを使用した宣言的なバリデーション
- カスタムバリデータのサポート: 独自のバリデーションロジックを簡単に追加
- 型安全: Go の型システムを活用したバリデーション
- エラーメッセージのカスタマイズ: フィールドごとのエラーメッセージ設定
- マップのバリデーション: 構造体だけでなくマップのバリデーションも可能
インストール
# 最新版
go get github.com/asaskevich/govalidator/v11
# 特定のバージョン
go get gopkg.in/asaskevich/govalidator.v10
基本的な使い方
パッケージのインポート
import "github.com/asaskevich/govalidator/v11"
// エイリアスを使用する場合
import (
valid "github.com/asaskevich/govalidator/v11"
)
構造体のバリデーション
type User struct {
Name string `valid:"alpha,required"`
Email string `valid:"email,required"`
Age int `valid:"range(0|120)"`
Phone string `valid:"numeric,length(10|11)"`
Website string `valid:"url,optional"`
}
user := User{
Name: "TaroYamada",
Email: "[email protected]",
Age: 25,
Phone: "09012345678",
Website: "https://example.com",
}
result, err := govalidator.ValidateStruct(user)
if err != nil {
fmt.Println("バリデーションエラー:", err.Error())
} else {
fmt.Println("バリデーション成功")
}
組み込みバリデータ
文字列バリデータ
// メールアドレス
govalidator.IsEmail("[email protected]") // true
// URL
govalidator.IsURL("https://example.com") // true
// アルファベット
govalidator.IsAlpha("HelloWorld") // true
// 数値文字列
govalidator.IsNumeric("12345") // true
// 日本語を含むマルチバイト文字
govalidator.IsMultibyte("こんにちは") // true
// Base64
govalidator.IsBase64("SGVsbG8gV29ybGQ=") // true
ネットワーク関連
// IPアドレス
govalidator.IsIP("192.168.1.1") // true
govalidator.IsIPv4("192.168.1.1") // true
govalidator.IsIPv6("2001:db8::1") // true
// MACアドレス
govalidator.IsMAC("00:00:5e:00:53:01") // true
// ポート番号
govalidator.IsPort("8080") // true
// CIDR
govalidator.IsCIDR("192.168.1.0/24") // true
識別子とフォーマット
// UUID
govalidator.IsUUID("550e8400-e29b-41d4-a716-446655440000") // true
// クレジットカード番号
govalidator.IsCreditCard("4111111111111111") // true
// ISBN
govalidator.IsISBN10("0596529260") // true
govalidator.IsISBN13("9780596529260") // true
// JSON
govalidator.IsJSON(`{"name":"value"}`) // true
// 緯度・経度
govalidator.IsLatitude("35.6762") // true
govalidator.IsLongitude("139.6503") // true
構造体タグの詳細
基本的なタグ
type Product struct {
// 必須フィールド
Name string `valid:"required"`
// オプショナルフィールド
Description string `valid:"optional"`
// バリデーションをスキップ
InternalID string `valid:"-"`
// 複数のバリデータ
SKU string `valid:"alphanum,required,length(8|12)"`
// カスタムエラーメッセージ
Price float64 `valid:"required~価格は必須です,range(0|999999)~価格は0〜999999の範囲で入力してください"`
}
パラメータ付きバリデータ
type ValidationExample struct {
// 範囲指定
Score int `valid:"range(0|100)"`
// 文字列長(バイト数)
Username string `valid:"length(3|20)"`
// 文字列長(文字数)
DisplayName string `valid:"runelength(1|10)"`
// 正規表現マッチ
Code string `valid:"matches(^[A-Z]{3}[0-9]{4}$)"`
// 選択肢から選ぶ
Status string `valid:"in(active|inactive|pending)"`
// 型チェック
Data interface{} `valid:"type(string)"`
}
カスタムバリデータ
シンプルなカスタムバリデータ
// 日本の郵便番号バリデータ
govalidator.TagMap["jpzipcode"] = govalidator.Validator(func(str string) bool {
return govalidator.Matches(str, "^\\d{3}-\\d{4}$")
})
type Address struct {
ZipCode string `valid:"jpzipcode,required"`
}
パラメータ付きカスタムバリデータ
// 特定の値と一致するかチェック
govalidator.ParamTagMap["equals"] = govalidator.ParamValidator(func(str string, params ...string) bool {
if len(params) > 0 {
return str == params[0]
}
return false
})
govalidator.ParamTagRegexMap["equals"] = regexp.MustCompile("^equals\\(([^)]+)\\)$")
type Config struct {
Environment string `valid:"equals(production)|equals(development)"`
}
高度なカスタムバリデータ(コンテキスト対応)
type User struct {
Password string `valid:"required,length(8|100)"`
PasswordConfirm string `valid:"passwordmatch"`
}
// 他のフィールドを参照するバリデータ
govalidator.CustomTypeTagMap.Set("passwordmatch", func(i interface{}, context interface{}) bool {
switch v := context.(type) {
case User:
return i.(string) == v.Password
}
return false
})
エラーハンドリング
詳細なエラー情報の取得
result, err := govalidator.ValidateStruct(user)
if err != nil {
// 個別のエラーを取得
errs := err.(govalidator.Errors).Errors()
for _, e := range errs {
fmt.Printf("エラー: %s\n", e.Error())
}
// フィールドごとのエラー
errorsByField := govalidator.ErrorsByField(err)
for field, errMsg := range errorsByField {
fmt.Printf("フィールド '%s': %s\n", field, errMsg)
}
}
高度な使い方
デフォルトの挙動設定
func init() {
// すべてのフィールドにバリデーションタグを必須にする
govalidator.SetFieldsRequiredByDefault(true)
// required タグで nil を許可する
govalidator.SetNilPtrAllowedByRequired(true)
}
マップのバリデーション
// バリデーションテンプレート
var userTemplate = map[string]interface{}{
"name": "required,alpha",
"email": "required,email",
"age": "required,int,range(0|120)",
"address": map[string]interface{}{
"street": "required,alphanum",
"city": "required,alpha",
"zipcode": "required,matches(^\\d{3}-\\d{4}$)",
},
}
// 入力データ
inputData := map[string]interface{}{
"name": "Taro",
"email": "[email protected]",
"age": 30,
"address": map[string]interface{}{
"street": "Shibuya1234",
"city": "Tokyo",
"zipcode": "150-0002",
},
}
result, err := govalidator.ValidateMap(inputData, userTemplate)
サニタイゼーション
// HTMLタグの除去
cleaned := govalidator.RemoveTags("<p>Hello World</p>") // "Hello World"
// ホワイトリスト
filtered := govalidator.WhiteList("abc123!@#", "a-z") // "abc"
// ブラックリスト
filtered := govalidator.BlackList("abc123", "0-9") // "abc"
// トリミング
trimmed := govalidator.Trim(" hello ", " ") // "hello"
// メールアドレスの正規化
normalized, _ := govalidator.NormalizeEmail("[email protected]")
// "[email protected]"
実用的な例
APIリクエストのバリデーション
type CreateUserRequest struct {
Username string `json:"username" valid:"required,alphanum,length(3|20)"`
Email string `json:"email" valid:"required,email"`
Password string `json:"password" valid:"required,length(8|100)"`
Age int `json:"age" valid:"required,range(13|120)"`
Terms bool `json:"terms" valid:"required"`
}
func handleCreateUser(req CreateUserRequest) error {
// バリデーション実行
if _, err := govalidator.ValidateStruct(req); err != nil {
return fmt.Errorf("入力データが無効です: %w", err)
}
// ユーザー作成処理
return createUser(req)
}
設定ファイルのバリデーション
type DatabaseConfig struct {
Host string `valid:"required,host"`
Port int `valid:"required,port"`
Username string `valid:"required,alphanum"`
Password string `valid:"required"`
Database string `valid:"required,alphanum"`
SSL bool `valid:"-"`
}
type AppConfig struct {
Database DatabaseConfig `valid:"required"`
ServerPort int `valid:"required,port"`
Environment string `valid:"required,in(development|staging|production)"`
LogLevel string `valid:"required,in(debug|info|warn|error)"`
}
パフォーマンス考慮事項
- バリデーションのキャッシュ: 同じ構造体を繰り返しバリデーションする場合、結果をキャッシュすることを検討
- 必要なバリデータのみ使用: 不要なバリデーションは避ける
- カスタムバリデータの最適化: 複雑な正規表現は事前にコンパイル
ベストプラクティス
- 明確なエラーメッセージ: カスタムエラーメッセージを使用してユーザーフレンドリーなフィードバックを提供
- バリデーションの分離: ビジネスロジックとバリデーションロジックを分離
- テスト: カスタムバリデータには必ずテストを作成
- ドキュメント化: カスタムバリデータの動作を明確にドキュメント化