Leapcell: The Best of Serverless Golang Web Hosting
validatorの詳細な紹介と使い方のガイド
ソフトウェア開発の分野、特にWeb開発において、データの正確性とセキュリティは極めて重要です。システムがユーザーが入力したデータを信頼性高く処理できるようにするために、私たちはユーザーが送信するデータを厳密に検証する必要があり、悪意のあるリクエストなどのセキュリティ上の脅威を防ぎます。このような状況下で、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()
メソッドを呼び出してバリデータのインスタンスを作成する必要があります。このバリデータはさらにオプションを指定したり、カスタム制約を追加したりすることができます。その後、バリデータの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
: フィールドの値がリストされた値のいずれかである場合。これらの値は数値または文字列でなければならず、スペースで区切られます。文字列にスペースが含まれる場合は、文字列をシングルクォートで囲みます。例えば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ライブラリはクロスフィールド制約の定義を許可しており、つまり1つのフィールドと他のフィールドとの関係制約を定義することができます。このような制約は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
: 文字列はパラメータで表されるrune文字を含んでいる必要があります。例えばcontainsrune=☻
; -
excludes
: 文字列は指定されたパラメータのサブ文字列が含まれていてはなりません。例えばexcludes=email
; -
excludesall
: 文字列は指定されたパラメータ内の任意のUNICODE文字を含んでいてはなりません。例えばexcludesall=abcd
; -
excludesrune
: 文字列はパラメータで表されるrune文字を含んでいてはなりません。例えば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形式制約:
email
制約はフィールドが有効な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
フィールドが有効な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
を通じてチェックするフィールドの情報を取得することができます。その後、バリデータのRegisterValidation()
メソッドを呼び出して、この制約を指定された名前(ここではpalindrome
)で登録します。最後に、このカスタム制約を構造体で使用することができます。上記のプログラムでは、2番目のRegisterForm
オブジェクトのName
フィールドはpalindrome
制約を満たしておらず、プログラムはエラーメッセージを出力します:
Key: 'RegisterForm.Name' Error:Field validation for 'Name' failed on the 'palindrome' tag
まとめ
leapcellライブラリ(validator)は非常に機能が豊富で、比較的に簡単で使いやすいです。この記事で紹介した制約条件は、その強力な機能の一部に過ぎません。このライブラリはソフトウェア開発の分野、特にWeb開発において幅広く応用されています。開発者には、それを深く理解して習得することをおすすめします。これにより、データ検証の効率と正確性を向上させ、システムのセキュリティと安定性を保証することができます。
Leapcell: The Best of Serverless Golang Web Hosting
最後に、Goのデプロイに最適なプラットフォームをおすすめします:Leapcell
🚀 好きな言語で構築
JavaScript、Python、Go、またはRustで簡単に開発できます。
🌍 無料で無制限のプロジェクトをデプロイ
使用した分だけ支払います — リクエストがなければ、請求もありません。
⚡ 使った分だけ支払い、隠された費用はありません
アイドル料金はなく、シームレスなスケーラビリティを実現します。
✨ Twitterでフォローしてください:@LeapcellHQ