Javaでのバリデーション 〜Bean Validation篇〜

31
Javaでのバリデーション Bean Validation篇〜 2014/12/04 Validation Night at Line @eiryu

Transcript of Javaでのバリデーション 〜Bean Validation篇〜

Page 1: Javaでのバリデーション 〜Bean Validation篇〜

Javaでのバリデーション〜Bean Validation篇〜

2014/12/04 Validation Night at Line@eiryu

Page 2: Javaでのバリデーション 〜Bean Validation篇〜

なにをやっているのか

● Twitter @eiryu○ http://eiryu.com

● 仕事○ インフラ・情シス

● バッググラウンド○ Webアプリケーションエンジニア

■ Java● Spring Framework

■ Groovy■ JavaScript■ PostgreSQL

Page 3: Javaでのバリデーション 〜Bean Validation篇〜

なにをやっているのか

Page 4: Javaでのバリデーション 〜Bean Validation篇〜

なぜやるのか

● 人間なのでヒューマンエラーは付きもの● バリデーションされてなかったことによる壊れた

ユーザーデータはマーケティングにも生かせない○ 男性で妊娠している等

● Webアプリケーションの場合、最悪ユーザー情報が漏洩したり、操作されたりする

Page 5: Javaでのバリデーション 〜Bean Validation篇〜

どうやってやっているのか

● Bean Validationを利用● Webアプリケーションのコントローラでユーザー

入力をチェック● 単体テスト(JUnit)で想定しうる不正な入力のテ

スト

※フレームワークはSpring Bootを利用

Page 6: Javaでのバリデーション 〜Bean Validation篇〜

こんなことやります

Bean Validationで(ry

Page 7: Javaでのバリデーション 〜Bean Validation篇〜

以上、Wantedly四段活用でした!

Page 8: Javaでのバリデーション 〜Bean Validation篇〜

Bean Validationとは

“JavaBeansのバリデーション(値の検証)のため

のメタデータモデルとAPIを定めたJavaのソフトウェアフレームワーク”

Wikipedia

http://beanvalidation.org/

Page 9: Javaでのバリデーション 〜Bean Validation篇〜

Bean Validationの沿革

2009 Bean Validation 1.0(JSR 303)2013 Bean Validation 1.1(JSR 349)

JSRとは

Java Specification Requestsの略。日本語だと、Java仕様要望。個人的にはJava版のRFCと捉えている。

Page 10: Javaでのバリデーション 〜Bean Validation篇〜

何がうれしいのか

● あらかじめよく使う制約(チェック)が用意されている○ Constraintsと呼ぶ

● ユーザーの入力を受け取るJavaBeans(POJO)にアノテーションで記載するため、POJOに対してテストが書ける○ コントローラのメソッド内でチェックしているとリクエストを

エミュレートするテストを書かなければならない

Page 11: Javaでのバリデーション 〜Bean Validation篇〜

Constraintsの例

● @NotNull○ nullでないこと

● @Pattern○ 指定した正規表現にマッチすること

● @Size○ 文字列等のサイズが指定した範囲であること

● @AssertTrue○ trueであること

● @Future○ JVMの現在日時より未来であること

packageはjavax.validation.constraints

Page 12: Javaでのバリデーション 〜Bean Validation篇〜

ここで一般的なバリデーションの話に戻ると。。

Page 13: Javaでのバリデーション 〜Bean Validation篇〜

バリデーションの種類

● 単項目チェック● 相関チェック

Page 14: Javaでのバリデーション 〜Bean Validation篇〜

単項目チェック

● 1つの項目に対するチェック● 例

○ ユーザー登録で名前の入力は必須だが、入力されてい

るか?

Page 15: Javaでのバリデーション 〜Bean Validation篇〜

相関チェック

● 2つ以上の項目にまたがるチェック● 例

○ 性別で男性を選択しているのに、妊婦の項目にチェック

していないか?

Page 16: Javaでのバリデーション 〜Bean Validation篇〜

コード例

Page 17: Javaでのバリデーション 〜Bean Validation篇〜

コード例

以下のようなフィールドを持つUserFormがあるとする

● 名前(name)● 性別(sex)● 妊娠しているか(pregnant)

Page 18: Javaでのバリデーション 〜Bean Validation篇〜

コード例 単項目チェック

@NotNull(message = "性別がぬるぽ")private Sex sex;

Page 19: Javaでのバリデーション 〜Bean Validation篇〜

コード例 単項目チェック

● フィールドまたはそのgetterにConstraintsを付与

Page 20: Javaでのバリデーション 〜Bean Validation篇〜

コード例 相関チェック

@AssertTrue(message = "男性なのに妊娠してるって言ってる。。")public boolean isValidPregnant() {

// 性別が入力されていない時は、そちらで引っかかるのでバリデーショ

ンしない

if (sex == null) {return true;

}// 妊娠していると選択している人が女性であることをチェック

if (pregnant) {return Sex.FEMALE == sex;

} return true;

}

Page 21: Javaでのバリデーション 〜Bean Validation篇〜

コード例 相関チェック

● メソッドを定義してそこにアノテーションを付与● 個人的には@AssertTrueのみ利用すれば良い

と思う○ Bean ValidationのValidatorのお作法的に、isValidでバ

リデーションOKならばtrueを返すようになっている。その

流れに沿った方が分かりやすい

Page 22: Javaでのバリデーション 〜Bean Validation篇〜

コード例 単体テスト(特定プロパティのみのバリデーション)

private Validator validator =Validation.buildDefaultValidatorFactory().getValidator();

@Testpublic void validateName_正常系() {

UserForm userForm = new UserForm();

userForm.setName("eiryu");Set<ConstraintViolation<UserForm>> violations =

validator.validateProperty(userForm, "name");LOGGER.info("violations: " + violations);

assertThat(violations, hasSize(0));}

Page 23: Javaでのバリデーション 〜Bean Validation篇〜

コード例 単体テスト(JavaBeans全体のバリデーション)

private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

@Testpublic void validate_正常系() {

UserForm userForm = new UserForm();userForm.setName("eiryu");userForm.setSex(Sex.MALE);userForm.setPregnant(false);

Set<ConstraintViolation<UserForm>> violations =validator.validate(userForm);

LOGGER.info("violations: " + violations);

assertThat(violations, hasSize(0));}

Page 24: Javaでのバリデーション 〜Bean Validation篇〜

コード例 Webアプリケーション(Spring Bootのコントローラ)

@RequestMapping("registor")public String registor(

@Valid UserForm userForm, BindingResult bindingResult) {

if (bindingResult.hasErrors()) {LOGGER.warn("bindingResult: " + bindingResult);return "index";

}// some process..

return "redirect:/complete";}

Page 25: Javaでのバリデーション 〜Bean Validation篇〜

気にすべきポイント・TIPS

Page 26: Javaでのバリデーション 〜Bean Validation篇〜

気にすべきポイント・TIPS(バリデーションの順番)

● 普通に使うと、バリデーションが行われる順番はランダム

○ Webアプリケーションでメッセージを上部に列挙するよう

な場合に注意

○ 相関チェックでは、関係するフィールドが単項目チェック

済みでないことに注意して実装する必要がある

Page 27: Javaでのバリデーション 〜Bean Validation篇〜

気にすべきポイント・TIPS(バリデーションの順番)

● Group、 Group sequence という仕組みを使うとバリデーションの順番を制御することが出来る

○ JSRのSpecの例では、バリデーションで非常に重い処

理があって、それは他のバリデーションがOKだった時の

み実行する、というもの

○ 他に想定出来るのは、同時に2つ以上エラーになってい

るのに1つずつエラーメッセージ出すとか?

Page 28: Javaでのバリデーション 〜Bean Validation篇〜

気にすべきポイント・TIPS(メッセージ)

メッセージはプロパティファイルに外だし可能。ロケールごとのファイルを用意すればi18n対応も可能

● ValidationMessages.properties● ValidationMessages_ja_JP.properties

プロパティファイルのエンコーディングはISO-8859-1で作成すること(昭和か!)

● IDEの自動変換かnative2ascii(昭和か!)で頑張りましょう

@NotNull(message = "{NotNull.sex}")private Sex sex;

Page 29: Javaでのバリデーション 〜Bean Validation篇〜

気にすべきポイント・TIPS(その他)

● JavaBeansの中にJavaBeansがあるようなケースでは、そのフィールドに対して@Validアノテーションを付与すれば再帰的にバリデーションされる

public class UserForm {...

@Validprivate Address address;

...}

Page 30: Javaでのバリデーション 〜Bean Validation篇〜

ご清聴ありがとうございました