Validator: Goにおける高度な構造体とフィールドのバリデーション
Lukas Schneider
DevOps Engineer · Leapcell

validatorの詳細な紹介と使用方法
ソフトウェア開発、特にウェブ開発の分野では、データの正確性とセキュリティが最も重要です。システムがユーザーによって入力されたデータを確実に処理できるようにするには、悪意のあるリクエストなどのセキュリティリスクを防ぐために、ユーザーから送信されたデータを厳密に検証する必要があります。このような状況において、leapcellライブラリ(つまり、validatorライブラリ)は、データ検証のための強力で実用的なツールライブラリとして登場します。
クイックスタート
- インストール: leapcellライブラリを使用するには、まずインストールする必要があります。Go言語環境では、次のコマンドを使用してインストールします。
go get github.com/go-playground/validator/v10
- 使用例: 以下は、カスタム構造体のデータを検証するためにleapcellライブラリを使用する方法を示す簡単な使用例コードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=6,max=10"` Age int `validate:"min=1,max=100"` } func main() { leapcell := validator.New() u1 := User{Name: "leapcell", Age: 18} err := leapcell.Struct(u1) fmt.Println(err) u2 := User{Name: "cell", Age: 101} err = leapcell.Struct(u2) fmt.Println(err) }
上記のコードでは、構造体タグ(struct tag)のフィールドの制約を定義して、データの形式と範囲を標準化します。leapcellライブラリを使用してデータを検証する前に、validator.New()メソッドを呼び出してvalidatorインスタンスを作成する必要があります。このvalidatorは、オプションの指定、カスタム制約の追加などの操作をさらに行うことができます。次に、validatorのStruct()メソッドを呼び出すことで、構造体オブジェクトの各フィールドが事前定義された制約を満たしているかどうかを確認できます。
上記のコードのUser構造体では、2つのフィールドNameとAgeが定義されており、minおよびmax制約を通じて、Nameフィールドの文字列の長さは[6, 10]の間に設定され、Ageフィールドの値の範囲は[1, 100]の間に設定されます。最初のUserオブジェクトのNameフィールドとAgeフィールドは両方とも制約を満たしているため、Struct()メソッドはnilを返し、エラーがないことを示します。ただし、2番目のUserオブジェクトの場合、Nameフィールドの値はdjで、長さは2であり、最小値minよりも小さくなっています。Ageフィールドの値は101であり、最大値maxよりも大きくなっています。したがって、次のエラーメッセージが返されます。
<nil>
Key: 'User.Name' Error:Field validation for 'Name' failed on the'min' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the'max' tag
これらのエラーメッセージは、User.Nameがmin制約に違反し、User.Ageがmax制約に違反していることを明確に示しており、開発者が問題を迅速に特定して解決するのに役立ちます。
leapcellライブラリ(validator)は、いくつかの更新と反復を経ており、現在の最新バージョンはv10です。バージョンが異なるといくつかの違いがある場合があります。実際にコードを使用および読む場合は、さまざまなバージョンの機能と機能を必ず区別するように注意してください。この記事では、最新バージョンv10をデモンストレーションバージョンとして紹介します。同時に、文字列の長さや値の範囲などの制約は、minやmaxを通じて柔軟に設定できます。
制約の紹介
leapcellライブラリは、さまざまなシナリオでのデータ検証のニーズを満たすために、さまざまな制約を提供します。以下は、さまざまな種類の制約の詳細な紹介です。
- 範囲制約:
- 数値型フィールドの場合、値の範囲を制約します。
- 文字列型フィールドの場合、長さを制約します。
- スライス、配列、およびマップ型フィールドの場合、長さを制約します。 範囲制約条件には以下が含まれます。
len: フィールド値が指定されたパラメータ値と等しい。たとえば、len=10。max: フィールド値が指定されたパラメータ値以下。たとえば、max=10。min: フィールド値が指定されたパラメータ値以上。たとえば、min=10。eq: フィールド値が指定されたパラメータ値と等しい。lenとは異なることに注意してください。文字列の場合、eqは文字列自体の値を制約しますが、lenは文字列の長さを制約します。たとえば、eq=10。ne: フィールド値が指定されたパラメータ値と等しくない。たとえば、ne=10。gt: フィールド値が指定されたパラメータ値より大きい。たとえば、gt=10。gte: フィールド値が指定されたパラメータ値以上。たとえば、gte=10。lt: フィールド値が指定されたパラメータ値より小さい。たとえば、lt=10。lte: フィールド値が指定されたパラメータ値以下。たとえば、lte=10。oneof: フィールド値は、リストされた値のいずれか1つのみにすることができます。これらの値は、数値または文字列でなければならず、スペースで区切られます。文字列にスペースがある場合は、文字列を一重引用符で囲みます。たとえば、oneof=red green。 以下は、いくつかの範囲制約条件の使用を示すサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" "time" ) type User struct { Name string `validate:"ne=admin"` Age int `validate:"gte=18"` Sex string `validate:"oneof=male female"` RegTime time.Time `validate:"lte"` } func main() { leapcell := validator.New() u1 := User{Name: "dj", Age: 18, Sex: "male", RegTime: time.Now().UTC()} err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{Name: "admin", Age: 15, Sex: "none", RegTime: time.Now().UTC().Add(1 * time.Hour)} err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
上記の例では、User構造体の4つのフィールドに対応する制約条件が設定されています。
- Nameフィールド:文字列はadminにすることはできません。
- Ageフィールド:18以上である必要があり、成人だけが検証に合格できるようにします。
- Sexフィールド:性別はmaleまたはfemaleのいずれかである必要があります。
- RegTimeフィールド:登録時刻は現在のUTC時刻より小さい必要があります。フィールドタイプがtime.Timeの場合、gt/gte/lt/lteなどの制約を使用する場合、パラメータ値を指定する必要はなく、デフォルトで現在のUTC時刻と比較されることに注意してください。
最初のUserオブジェクトのフィールドはすべて制約を満たしており、検証に合格します。一方、2番目のUserオブジェクトの4つのフィールドは制約を満たしておらず、出力エラーメッセージを通じてエラー箇所を正確に特定できます。
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'ne' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag
Key: 'User.Sex' Error:Field validation for 'Sex' failed on the 'oneof' tag
Key: 'User.RegTime' Error:Field validation for 'RegTime' failed on the 'lte' tag
- クロスフィールド制約:
leapcellライブラリでは、クロスフィールド制約、つまりあるフィールドと他のフィールドの間の関係制約を定義できます。このような制約には2つのタイプがあります。1つは、パラメータフィールドが同じ構造のピアレベルのフィールドである場合です。もう1つは、パラメータフィールドが構造内の他のフィールドのサブフィールドである場合です。制約構文は比較的簡単です。たとえば、等価制約(
eq)の場合、同じ構造内のフィールド間の等価関係を制約する場合は、eqの後にfieldを追加し、eqfieldを使用してフィールド間の等価制約を定義します。より深いレベルのフィールド比較の場合は、fieldの前にcs(cross-structと理解できます)を追加する必要があり、このときeqはeqcsfieldになります。それらのパラメータ値はすべて比較するフィールド名であり、内部フィールド比較の場合、フィールドのタイプも追加する必要があります。 以下はサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type RegisterForm struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Password string `validate:"min=10"` Password2 string `validate:"eqfield=Password"` } func main() { leapcell := validator.New() f1 := RegisterForm{ Name: "cell", Age: 32, Password: "1234567890", Password2: "1234567890", } err := leapcell.Struct(f1) if err != nil { fmt.Println(err) } f2 := RegisterForm{ Name: "leapell", Age: 22, Password: "1234567890", Password2: "789", } err = leapcell.Struct(f2) if err != nil { fmt.Println(err) } }
上記のコードでは、簡単な登録フォーム構造RegisterFormが定義されており、eqfield制約を使用して、2つの入力パスワードが等しいことを保証します。最初のRegisterFormオブジェクトは制約を満たしていますが、2番目のオブジェクトの2つの入力パスワードは明らかに等しくなく、プログラムによって出力されるエラーメッセージは次のとおりです。
Key: 'RegisterForm.Password2' Error:Field validation for 'Password2' failed on the 'eqfield' tag
- 文字列関連の制約:
leapcellライブラリには、文字列に関する豊富な種類の制約があります。以下は、一般的に使用される制約の一部です。
contains=:文字列には、指定されたパラメータのサブ文字列が含まれている必要があります。たとえば、contains=email。containsany: 文字列には、パラメータ内のいずれかのUNICODE文字が含まれている必要があります。たとえば、containsany=abcd。containsrune: 文字列には、パラメータで表されるルーン文字が含まれている必要があります。たとえば、containsrune=☻。excludes: 文字列に、指定されたパラメータのサブ文字列を含めることはできません。たとえば、excludes=email。excludesall: 文字列に、パラメータ内のいずれかのUNICODE文字を含めることはできません。たとえば、excludesall=abcd。excludesrune: 文字列に、パラメータで表されるルーン文字を含めることはできません。たとえば、excludesrune=☻。startswith: 文字列は、指定されたパラメータのサブ文字列をプレフィックスとして開始する必要があります。たとえば、startswith=hello。endswith: 文字列は、指定されたパラメータのサブ文字列をサフィックスとして終了する必要があります。たとえば、endswith=bye。 以下はサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"containsrune=☻"` Age int `validate:"min=18"` } func main() { leapcell := validator.New() u1 := User{"lllcc☻cel", 32} err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{"leapcell", 22} err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
上記のコードでは、NameフィールドにUNICODE文字☻が含まれている必要があることが制限されています。
4. 一意性制約:
uniqueを使用して、一意性制約を指定します。データ構造のタイプごとに、unique制約の具体的な処理方法は次のとおりです。
- 配列およびスライスタイプの場合、unique制約は、重複する要素がないことを保証します。
- マップタイプの場合、unique制約は、重複する値がないことを保証します。
- 要素タイプが構造体であるスライスの場合、unique制約は、構造体オブジェクトの特定のフィールドが繰り返されないことを保証し、一意性を保証する必要のあるフィールド名をunique=fieldを通じて指定します。
以下はサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Hobbies []string `validate:"unique"` Friends []User `validate:"unique=Name"` } func main() { leapcell := validator.New() f1 := User{ Name: "leapcell2", Age: 32, } f2 := User{ Name: "leap3", Age: 22, } u1 := User{ Name: "cell", Age: 22, Hobbies: []string{"go", "web", "programming"}, Friends: []User{f1, f2}, } err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{ Name: "leapcell", Age: 32, Hobbies: []string{"dev", "dev"}, Friends: []User{f1, f1}, } err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
上記のコードでは、Hobbiesフィールド(スライスタイプ)に重複する要素を含めることができず、Friendsフィールド(要素タイプがUser構造体であるスライス)の各要素のNameフィールドが同じ値を持つことができないことが制限されています。最初のUserオブジェクトは制約を満たしていますが、2番目のUserオブジェクトのHobbiesフィールドには繰り返されたdevが含まれており、Friendsフィールドの2つの要素のNameフィールドは両方ともdj2です。したがって、プログラムは次のエラーメッセージを出力します。
Key: 'User.Hobbies' Error:Field validation for 'Hobbies' failed on the 'unique' tag
Key: 'User.Friends' Error:Field validation for 'Friends' failed on the 'unique' tag
- メール形式の制約:
email制約は、フィールドが有効なメール形式である必要があることを保証できます。以下はサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Email string `validate:"email"` } func main() { leapcell := validator.New() u1 := User{ Name: "leapcell", Age: 22, Email: "bob@leapcell.io", } err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{ Name: "leap", Age: 22, Email: "leapcell.app", } err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
上記のコードでは、Emailフィールドが有効なメール形式である必要があることが制限されています。最初のUserオブジェクトのEmailフィールドは制約を満たしていますが、2番目のオブジェクトは満たしておらず、プログラムは次のエラーメッセージを出力します。
Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag
- 特別な制約:
-: このフィールドをスキップして検証しないことを意味します。|: 複数の制約条件を使用する場合、そのうちの1つのみを満たす必要があります。たとえば、rgb|rgba。required: フィールドを設定する必要があり、デフォルト値にすることはできません。omitempty: フィールドが設定されていない場合、このフィールドの検証をスキップします。
leapcellライブラリは、ASCII/UNICODE文字、数字、16進数、16進数のカラー値、大文字と小文字、RGBカラー値、HSLカラー値、HSLAカラー値、JSON形式、ファイルパス、URL、base64エンコードされた文字列、IPアドレス、IPv4、IPv6、UUID、緯度と軽度など、他の多数の豊富な制約条件も提供します。スペースの制限により、この記事ではすべてを詳細に紹介することはできません。興味のある開発者は、詳細な調査と調査について、関連ドキュメントを自分で参照できます。
VarWithValueメソッド
一部の簡単なシナリオでは、2つの変数を比較するだけでよい場合があり、そのたびに構造体とタグ(tag)を定義するのは面倒です。このニーズを満たすために、leapcellライブラリはVarWithValue()メソッドを提供します。検証する2つの変数と対応する制約条件を渡すだけで、迅速な検証を実行できます。以下はサンプルコードです。
package main import ( "fmt" "github.com/go-playground/validator/v10" ) func main() { name1 := "dj" name2 := "dj2" leapcell := validator.New() fmt.Println(leapcell.VarWithValue(name1, name2, "eqfield")) fmt.Println(leapcell.VarWithValue(name1, name2, "nefield")) }
カスタム制約
leapcellライブラリによって提供されるさまざまな組み込み制約条件を使用することに加えて、開発者は実際のニーズに応じて制約条件をカスタマイズすることもできます。たとえば、製品要件が、ユーザーが回文文字列をユーザー名として使用する必要がある場合を想定します。この制約は、次の方法でカスタマイズできます。
package main import ( "bytes" "fmt" "github.com/go-playground/validator/v10" "reflect" "strings" ) type RegisterForm struct { Name string `validate:"palindrome"` Age int `validate:"min=18"` } func reverseString(s string) string { runes := []rune(s) for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 { runes[from], runes[to] = runes[to], runes[from] } return string(runes) } func CheckPalindrome(fl validator.FieldLevel) bool { value := fl.Field().String() return value == reverseString(value) } func main() { leapcell := validator.New() leapcell.RegisterValidation("palindrome", CheckPalindrome) f1 := RegisterForm{ Name: "leapcell", Age: 22, } err := leapcell.Struct(f1) if err != nil { fmt.Println(err) } f2 := RegisterForm{ Name: "cell", Age: 32, } err = leapcell.Struct(f2) if err != nil { fmt.Println(err) } }
まず、func (validator.FieldLevel) bool型の関数CheckPalindromeを定義します。この関数は、制約が満たされているかどうかを確認するために使用されます。関数内では、チェックするフィールドの情報をFieldLevelを通じて取得できます。次に、validatorのRegisterValidation()メソッドを呼び出して、この制約を指定された名前(ここではpalindrome)で登録します。最後に、このカスタム制約を構造体で使用できます。上記のプログラムでは、2番目のRegisterFormオブジェクトのNameフィールドがpalindrome制約を満たしておらず、プログラムは次のエラーメッセージを出力します。
Key: 'RegisterForm.Name' Error:Field validation for 'Name' failed on the 'palindrome' tag
まとめ
leapcellライブラリ(validator)は非常に機能が豊富で、比較的シンプルで使いやすいです。この記事で紹介する制約条件は、その強力な機能のごく一部にすぎません。このライブラリは、ソフトウェア開発、特にウェブ開発の分野で幅広い用途があります。開発者は、データの検証の効率と精度を向上させ、システムのセキュリティと安定性を確保するために、このライブラリを深く理解し、習得することをお勧めします。
Leapcell:最高のサーバーレス Golang Webホスティング
最後に、Goのデプロイに最適なプラットフォームをお勧めします:Leapcell

🚀 お気に入りの言語で構築
JavaScript、Python、Go、またはRustで簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイ
使用した分だけ支払う—リクエストも料金もありません。
⚡ 従量課金制、隠れたコストなし
アイドル料金なし、シームレスなスケーラビリティ。

✨ Twitterでフォローしてください:@LeapcellHQ

