What is it, naokirin?

Rustでvalidationを実行する

Webアプリケーションサーバフレームワークなどでは、受け付けたリクエストのデータが適切かをチェックするためのバリデーション機能があります。

Rustでは汎用的なバリデーションのクレートとして validator が存在しており、こちらを使う機会があったので今回はその紹介をします。

github.com

Validatorを導入する

Validatorの導入ですが、通常の Cargo.toml に以下を追加することで可能です。

[dependencies]
validator = { version = "0.14", features = ["derive"] }

READMEによると、導入には、Rust 1.42 以上が必須となっていますので、その点だけご注意ください。

validate を使えるようにする

struct に対して、 #[derive(Validate)] をつけることで、バリデーションの関数 validate が使えるようになります。

#[derive(Validate)]
struct User {
    pub name: String
}

User{ name: "Bob" }.validate();

validateResult<(), ValidationErrors> を返す関数です。バリデーションがすべて成功すれば、 Ok(()) 、 そうでなければ Err<ValidationErrors> が返ってきます。

現在は特に条件などがないため、 Ok(()) が返ってきます。

validatorを指定する

デフォルトで基本的なバリデーションの機能は用意されています。

validator 検証する内容 主な対象の型 引数
email 妥当なメールアドレス String
url 妥当なURL String
length 長さ String, Vec min, max, equal(1つ以上必須)
range 数値の範囲 i32, u32, f32 min, max(1つ以上必須)
must_match 2つのフィールドの一致 i32, String, Vec other
contains 文字列もしくはhashmapのキーに含まれる String, HashMap pattern
regex 正規表現に一致 String path
credit_card 妥当なクレジットカード番号 String
phone 妥当な電話番号 String
custom カスタムバリデータを実行 function, arg
non_control_character 制御文字を含まない String
required Someである Option
required_nested Someかつ含むインスタンスのバリデーションも成功する Option

実際に利用する際は以下のようにします。1つのフィールドに対して複数のバリデーションを指定できます。

#[derive(Validate)]
struct User {
    #[validate(required)]
    #[validate(length(min = 1, max = 24))]
    name: Option<String>
}

READMEにかなりわかりやすく書いてあるので、そこまで詰まることはないかと思います。

https://github.com/Keats/validator#validators

また、上記にくわえ、Validate を継承した型を利用しているフィールドで、その型のバリデーションも実行したい場合は以下のようにします。

#[validate]
attributes: Attributes

また、READMEだけだと少しわかりにくいものとして、 must_match があるかと思います。イメージとしては、パスワードと確認用の入力が一致しているかのような2つのフィールドの一致を検証したい際に利用します。

#[validate(must_match = "password")]
password: String,

#[validate(must_match(other = "password"))]
password_confirmation: String

また、デフォルトのエラーコードやメッセージを上書きすることができます。

#[validate(email(code = "error code"))]
foo: String,

#[validate(length(min = 1, max = 2, message = "message"))]
bar: String

カスタムバリデーション

クレートに用意されていないバリデーションは、 custom で実行することができます。

fn validate_exact_foo(value: &str) -> Result<(), ValidationError> {
    if value == "foo" {
        Ok(())
    } else {
        Err(ValidationError{
            code: Cow::from("exact_foo"),
            message: Some(Cow::from("Do not exact `foo`.")),
            params: Default::default()
        })
    }
}

#[derive(Validate)]
struct User {
    #[validate(function = "validate_exact_foo")]
    user: String
}

まとめ

validator は割と素直に使えるバリデーションのクレートで、必要十分な機能を備えているかと思います。

ただし、カスタムバリデータでは、 ValidationError を返す必要があるため複数のエラーを返すことができません。そのため、まとめるよりも極力分離して指定していく必要があります。 また定義時のバリデーション指定で、組み込みのバリデータのように引数を指定できるような形式で作ることができないのが少し残念なところではあります。

とはいえ、多少工夫すればそのあたりも問題になりにくいため、今後もRustでバリデーションが必要な場合は使っていきたいと思います。