TL;DR
Buf が開発している、Protocol Buffers のスキーマにバリデーションルールを書くためのライブラリ。protoc-gen-validate(PGV)の後継にあたる。
なぜ必要か
PGV でサポートしていた機能はすべてサポートしている。ルールの記述に CEL を採用しているため柔軟性が高く、さまざまなルールを設定できる。
対応言語は Go、C++、Java、Python、TypeScript である(TypeScript 版は 2024 年 10 月時点で未リリース)。Connect を使っている場合は、validate-go で、全リクエストをチェックするインターセプタを足せる。
仕組み
.proto ファイルで buf/validate/validate.proto を import し、option でルールを付ける。ルールは付ける場所で 3 段階に分かれる。
- Message レベル:
google.protobuf.MessageOptionsを拡張する。disabledをtrueにすると、そのメッセージのほかのルールが無効になる - Oneof レベル:
google.protobuf.OneofOptionsを拡張する。requiredをtrueにすると、oneof のどれか 1 つに値があることをチェックする - フィールドレベル:
google.protobuf.FieldOptionsを拡張し、データ型ごとのルールを付ける
フィールドレベルでは、文字列(StringRules)のルールが特に多い。値の一致(const、in)、長さ(min_len、max_bytes)、正規表現(pattern)や部分一致(prefix、contains)のほか、メールアドレス、ホスト名、IP アドレス、URI、UUID といった形式もチェックできる。
message OneofExample {
oneof union {
option (buf.validate.oneof).required = true;
string val1 = 1;
string val2 = 2;
}
}例
Go で、文字列の長さを 1 文字以上に制限する。buf.yaml の deps に buf.build/bufbuild/protovalidate を足して buf dep update を実行し、.proto ファイルにルールを書く。
import "buf/validate/validate.proto";
message Hello {
string hello = 1 [(buf.validate.field).string.min_len = 1];
}buf generate でコードを生成し、protovalidate-go で検証する。空文字なので失敗する。
v, err := protovalidate.New()
if err != nil {
log.Fatalln(err)
}
if err = v.Validate(&hellov1.Hello{Hello: ""}); err != nil {
log.Println("validation failed:", err)
}$ go run main.go
2024/10/01 15:25:20 validation failed: validation error:
- hello: value length must be at least 1 characters [string.min_len]自分の理解
フィールドレベルのルールは思ったより多い。