SlideShare a Scribd company logo
バリデート編
アジェンダ
 はじめに
 サンプルページ
 サンプルページの準備
 バリデート
 メッセージを変更
 カスタムバリデータ
 データベースを参照するバリデート
 ローカライズ
 まとめ
はじめに
 Spring Bootを使って、
Webの入力妥当性チェック(バリデート)の
実装方法を試してみる。
サンプルページ
 サンプルページの画面フローは下記の通り
商品入力画面
確認画面
完了画面
【確認】クリック
【登録】クリック
入力エラー時
【戻る】クリック
【戻る】クリック
サンプルページ
 サンプルページの画面フローは下記の通り
商品入力画面
確認画面
完了画面
【確認】クリック
【登録】クリック
入力エラー時
【戻る】クリック
【戻る】クリック
ここの話
どのように入力チェックをするか?
サンプルページ
 Controller
/** 商品関連のページを制御するController */
@Controller
@RequestMapping("product")
public class ProductController {
}
http://someserver/someapp/product/ に
関連付けたController
サンプルページの準備
 まずサンプルページの下記画面フローを作成する
商品入力画面
確認画面
完了画面
【確認】クリック
【登録】クリック
入力エラー時
【戻る】クリック
【戻る】クリック
サンプルページの準備
 Controller
/** 商品関連のページを制御するController */
@Controller
@RequestMapping("product")
public class ProductController {
/** 商品入力の開始 */
@RequestMapping(value = "/input", method= RequestMethod.GET)
public String input(Model model) {
return "product/input";
}
}
http://someserver/someapp/product/input の
GETリクエストを処理するメソッド
テンプレート product/input.html が
レンダリングされる
サンプルページの準備
 Template (input.html)
<body>
<h3>商品入力</h3>
<form action="confirm.html" method="post">
Code: <input type="text" name="productCode" size="20"/>
<br/>
商品名: <input type="text" name="productName" size="20"/>
<br/>
金額: <input type="text" name="price" size="20"/>
<br/>
<input type="submit" name="naviButton" value="確認"/>
</form>
</body>
素のHTML
サンプルページの準備
 Template (input.html)
<form action="confirm.html"
th:action="@{/product/confirm}" method="post">
Code:
<input type="text" name="productCode" size="20" />
<br/>
商品名:
<input type="text" name="productName" size="20"/>
<br/>
金額:
<input type="text" name="price" size="20" />
<br/>
<input type="submit" name="naviButton" value="確認"/>
</form>
Thymeleaf の記述を追加
サンプルページの準備
 Controller
/** 商品関連のページを制御するController */
@Controller
@RequestMapping("product")
public class ProductController {
/** 商品入力の確認 */
@RequestMapping(value = "/confirm", method=RequestMethod.POST)
public String confirm(Model model,) {
return "product/confirm";
}
}
http://someserver/someapp/product/input からの
POSTリクエストを処理するメソッド
テンプレート product/confirm.html が
レンダリングされる
サンプルページの準備
 Template (confirm.html)
<body>
<h3>商品入力 - 確認</h3>
<form action="finish.html" method="post">
Code:
<br/>
商品名:
<br/>
金額:
<br/>
<input type="submit" name="naviButton" value="登録"/>
</form>
</body>
サンプルページの準備
 動作確認
http://someserver/someapp/product/input
http://someserver/someapp/product/confirm
サンプルページの準備
 Template (input.html)
<form action="confirm.html"
th:action="@{/product/confirm}" method="post">
Code:
<input type="text" name="productCode" size="20"
th:field="*{productForm.productCode}"/>
<br/>
商品名:
<input type="text" name="productName" size="20"
th:field="*{productForm.productName}"/>
<br/>
金額:
<input type="text" name="price" size="20"
th:field="*{productForm.price}"/>
<br/>
入力画面を実装する
サンプルページの準備
 Form
/** 画面の値を保持するForm */
public class ProductForm {
private Integer productCode;
private String productName;
private Integer price;
// getter/setteを省略
フォームクラスを実装する
これが入力値の格納先となる
サンプルページの準備
 Controller
/** 商品関連のページを制御するController */
@Controller
@RequestMapping("product")
public class ProductController {
@ModelAttribute
public ProductForm setupForm() {
return new ProductForm();
}
/** 商品入力の確認 */
@RequestMapping(value = "/confirm", method=RequestMethod.POST)
public String confirm(Model model, ProductForm productForm) {
return "product/confirm";
}
}
画面用のFormクラスを初期化する
引数に入れた項目が
画面上の同名の項目にマップされる
サンプルページの準備
 Template (confirm.html)
<form action="finish.html" id="confirm"
th:object="${productForm}" method="post">
Code:
<span th:text="${productForm.productCode}"/>
<br/>
商品名:
<span th:text="${productForm.productName}"/>
<br/>
金額:
<span th:text="${productForm.price}"/>
<br/>
確認画面を実装する
サンプルページの準備
 動作確認
http://someserver/someapp/product/input
http://someserver/someapp/product/confirm
バリデート
 Formの修正
/** 画面の値を保持するForm */
public class ProductForm {
@NotNull
private Integer productCode;
@NotNull
@Length(max=10)
private String productName;
@NotNull
@Max(1000)
private Integer price;
// getter/setteを省略
Bean Validationの仕様に従って
アノテーションを記述する
@NotNull → 必須入力
@Length → 長さ制限(10文字まで)
@Max → 1,000までの入力
バリデート
 Controllerの修正
/** 商品関連のページを制御するController */
@Controller
@RequestMapping("product")
public class ProductController {
@RequestMapping(value = "/confirm", method=RequestMethod.POST)
public String confirm(Model model, @Valid ProductForm productForm,
Errors errors) {
if (errors.hasErrors()) {
return "product/input";
}
return "product/confirm";
}
}
@Validを付けて、Formに対して
バリデートをすることを明記
受け取ったバリデート結果を参照し、
エラーがあれば入力画面に遷移する
バリデート
 Templateの修正(input.html)
Code:
<input type="text" name="productCode" size="20"
th:field="*{productForm.productCode}"/>
<span th:if="${#fields.hasErrors('*{productForm.productCode}')}"
th:errors="*{productForm.productCode}" style="color: red"/> <br/>
商品名:
<input type="text" name="productName" size="20"
th:field="*{productForm.productName}"/>
<span th:if="${#fields.hasErrors('*{productForm.productName}')}"
th:errors="*{productForm.productName}" style="color: red"/> <br/>
金額:
<input type="text" name="price" size="20"
th:field="*{productForm.price}"/>
<span th:if="${#fields.hasErrors('*{productForm.price}')}"
th:errors="*{productForm.price}" style="color: red"/><br/>
エラーがあるか?
エラーがあれば、内容を出力
バリデート
 動作確認
http://someserver/someapp/product/input
未入力、
10文字超え、
1000超え
エラーメッセージを表示
メッセージを変更
 初期状態ではメッセージが全て英語のため、
これを日本語にする
メッセージを変更
 方法1:個別に指定する
public class ProductForm {
@NotNull(message="入力してください(埋め込み)")
private Integer productCode;
各バリデート用のアノテーションにある
messageプロパティで指定する
http://someserver/someapp/product/input
メッセージを変更
 方法2:プロパティファイルで指定する
javax.validation.constraints.NotNull.message=入力してください(プロパティ)
プロパティファイルに対応するアノテーションの
メッセージをプロパティファイルで指定する
http://someserver/someapp/product/input
ValidationMessages.properties
メッセージを変更
 可変項目をメッセージに埋め込む
org.hibernate.validator.constraints.Length.message=入力は{max}文字までです。
アノテーションのパラメータ名を
プロパティファイルで指定する
http://someserver/someapp/product/input
@NotBlank
@Length(min=1, max=10)
private String productName;
{max}に
10が埋め込まれる
カスタムバリデータ
 自前のバリデート機能を実装する方法は次の通り
(サンプルとして電話番号のバリデータを実装する)
カスタムバリデータ
 アノテーションを宣言する (お約束の書き方)
@Constraint(validatedBy = TelNumberValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TelNumber {
String message() default “TEL number is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
実際にチェックを行う
クラスを指定(後述)
デフォルトメッセージ
カスタムバリデータ
 チェック処理を実装する
public class TelNumberValidator
implements ConstraintValidator<TelNumber, String> {
@Override
public void initialize(TelNumber constraintAnnotation) {
}
@Override
public boolean isValid(final String value,
final ConstraintValidatorContext context) {
return true;
}
}
チェック対象のアノテーションと
チェック対象のデータ型
ここにチェック処理を
実装する
初期処理
カスタムバリデータ
 チェック処理を実装する
public class TelNumberValidator
implements ConstraintValidator<TelNumber, String> {
private Pattern pattern;
@Override
public void initialize(TelNumber constraintAnnotation) {
pattern = Pattern.compile("^0¥¥d*-¥¥d*-¥¥d*");
}
@Override
public boolean isValid(final String value,
final ConstraintValidatorContext context) {
Matcher matcher = pattern.matcher(value);
return matcher.find();
}
}
正規表現チェック用の
フィールド
パターンに合ってたら true
違ったら false を返す
初期処理
0から始まって、数値の間に「-」が2つあること
カスタムバリデータ
 カスタムバリデータを利用する
@NotBlank
@TelNumber
private String telNumber;
http://someserver/someapp/product/input
データベースを参照するバリデート
 今までのBean Validationは
Controllerの手前での出来事だったが、
データベースを参照するバリデートはControllerの先の動作となる
Controller
Service
ブラウザ
Bean Validation
データベースを参照する
バリデート
データベースを参照するバリデート
 Serviceにバリデート用メソッドを用意する
/** Productをチェックする */
public void validate(ProductForm productForm)
throws DuplicateProductException {
if ( productForm.getProductCode() == 1 ) {
throw new DuplicateProductException();
}
}
サンプルとして、商品コードに
「1」が入れられたら例外を投げる
異常時は例外を投げる
データベースを参照するバリデート
 サービス固有の例外を作成する
public class DuplicateProductException extends Exception {
}
Exceptionを拡張して
必ずキャッチさせる
データベースを参照するバリデート
 ControllerでServiceに追加したチェックを呼び出す
/** 商品入力の確認 */
@RequestMapping(value = "/confirm", method=RequestMethod.POST)
public String confirm(Model model,
@Valid ProductForm productForm, Errors errors) {
if (errors.hasErrors()) {
return "product/input";
}
try {
productService.validate(productForm);
} catch(DuplicateProductException e) {
errors.rejectValue("productCode", "duplicate",
new String[]{"商品コード"}, "default message.");
return "product/input";
}
return "product/confirm";
}
チェックを呼び出す
エラーがあったら
rejectValueでフィールド
ごとのエラーを詰める
データベースを参照するバリデート
 メッセージ用のプロパティファイルを用意する
duplicate={0}が重複しています。
「キー = メッセージ」の形式で記述
messages.properties
errors.rejectValue("productCode", "duplicate",
new String[]{"商品コード"}, "default message.");
rejectValueしたときのerrorCodeを
プロパティと同じキーにする
置換箇所「{0}」に
「商品コード」を当てる
データベースを参照するバリデート
 アプリケーション設定を変更する
spring:
messages:
basename: messages
先ほどのmessages.propertiesを
参照するよう設定する
application.yml
http://someserver/someapp/product/input
データベースを参照するバリデート
 その他の方法
if (errors.hasErrors()) {
return "product/input";
}
productService.validate(productForm);
return "product/confirm";
}
@ExceptionHandler( DuplicateProductException.class )
public ModelAndView handleException(RuntimeException e ) {
return new ModelAndView(" product/input ")
.addObject("error", e.getMessage());
}
public class DuplicateProductException extends RuntimeException {
}
RuntimeExceptionに変更
try ~ catchを削除
コントローラの例外を
一手に引き受ける
ローカライズ
 言語ごとのプロパティファイルを配置し、多言語対応をする
javax.validation.constraints.NotNull.message=Please input.
ValidationMessages.properties
デフォルト
javax.validation.constraints.NotNull.message=入力してください。
ValidationMessages_ja.properties
日本語
javax.validation.constraints.NotNull.message=Please input.
ValidationMessages_en.properties
英語
まとめ
 SpringというよりBean Validation(JSR-303,JSR-349)の仕様を
知る方が、学習の近道かもしれない・・・?
 また、Springの採用している実装のHibernate Validatorも確認を
まとめ
 参考
■JSR 303 Bean Validationで遊んでみるよ! - Yamkazu's Blog
http://yamkazu.hatenablog.com/entry/20110206/1296985545
■私のBeanValidationの使い方(Java EE Advent Calendar 2013) — 裏紙
http://backpaper0.github.io/2013/12/03/javaee_advent_calendar_2013.
html
■Spring Boot Security Application - Bartosz Kielczewski
http://kielczewski.eu/2014/12/spring-boot-security-application/
■81.参考: 妥当性チェックのエラーメッセージ出力方法 - soracane
https://sites.google.com/site/soracane/home/springnitsuite/spring-
batch/81-can-kao-tuo-dang-xingchekkunoeramesseji-chu-li-fang-fa

More Related Content

What's hot (20)

PDF
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
増田 亨
 
PDF
Spring Data RESTを利用したAPIの設計と、作り直しまでの道のり
Rakuten Group, Inc.
 
PPTX
Spring超入門-Springと出会ってから1年半-
Ryosuke Uchitate
 
PDF
Pythonによる黒魔術入門
大樹 小倉
 
PDF
Go言語によるwebアプリの作り方
Yasutaka Kawamoto
 
PPTX
さくっと理解するSpring bootの仕組み
Takeshi Ogawa
 
PDF
Redmineの画面をあなた好みにカスタマイズ - View customize pluginの紹介 - Redmine Japan 2020
onozaty
 
KEY
やはりお前らのMVCは間違っている
Koichi Tanaka
 
PDF
JSUG 20141127 「Spring Bootを用いたドメイン駆動設計」
Junichiro Kazama
 
PDF
これからSpringを使う開発者が知っておくべきこと
土岐 孝平
 
PDF
Spring Bootをはじめる時にやるべき10のこと
心 谷本
 
PPTX
CEDEC2021 ダウンロード時間を大幅減!~大量のアセットをさばく高速な実装と運用事例の共有~
SEGADevTech
 
PPTX
JIRA / Confluence の 必須プラグインはこれだ
Narichika Kajihara
 
PDF
Azure でサーバーレス、 Infrastructure as Code どうしてますか?
Kazumi OHIRA
 
PDF
これから始めるSpringのwebアプリケーション
土岐 孝平
 
PDF
HTMLを書くだけで誰でも簡単!A-FrameではじめるWeb AR/VR
Takashi Yoshinaga
 
PDF
RESTful Web アプリの設計レビューの話
Takuto Wada
 
PDF
Spring Day 2016 - Web API アクセス制御の最適解
都元ダイスケ Miyamoto
 
PDF
今さら聞けないDiとspring
土岐 孝平
 
PDF
中・大規模でLaravelを導入するTips
Kenjiro Kubota
 
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
増田 亨
 
Spring Data RESTを利用したAPIの設計と、作り直しまでの道のり
Rakuten Group, Inc.
 
Spring超入門-Springと出会ってから1年半-
Ryosuke Uchitate
 
Pythonによる黒魔術入門
大樹 小倉
 
Go言語によるwebアプリの作り方
Yasutaka Kawamoto
 
さくっと理解するSpring bootの仕組み
Takeshi Ogawa
 
Redmineの画面をあなた好みにカスタマイズ - View customize pluginの紹介 - Redmine Japan 2020
onozaty
 
やはりお前らのMVCは間違っている
Koichi Tanaka
 
JSUG 20141127 「Spring Bootを用いたドメイン駆動設計」
Junichiro Kazama
 
これからSpringを使う開発者が知っておくべきこと
土岐 孝平
 
Spring Bootをはじめる時にやるべき10のこと
心 谷本
 
CEDEC2021 ダウンロード時間を大幅減!~大量のアセットをさばく高速な実装と運用事例の共有~
SEGADevTech
 
JIRA / Confluence の 必須プラグインはこれだ
Narichika Kajihara
 
Azure でサーバーレス、 Infrastructure as Code どうしてますか?
Kazumi OHIRA
 
これから始めるSpringのwebアプリケーション
土岐 孝平
 
HTMLを書くだけで誰でも簡単!A-FrameではじめるWeb AR/VR
Takashi Yoshinaga
 
RESTful Web アプリの設計レビューの話
Takuto Wada
 
Spring Day 2016 - Web API アクセス制御の最適解
都元ダイスケ Miyamoto
 
今さら聞けないDiとspring
土岐 孝平
 
中・大規模でLaravelを導入するTips
Kenjiro Kubota
 

More from なべ (8)

PPTX
Javaで学ぶネットワークプログラミングの基礎
なべ
 
PPTX
Reladomoを使ったトランザクション履歴管理をプロダクトに適用した際のメリット/デメリット/課題など
なべ
 
PPTX
普通のJavaエンジニアが、なぜ技術書を出版するに至ったか?
なべ
 
PDF
Spring bootでweb セキュリティ(ログイン認証)編
なべ
 
PDF
Spring bootでweb ユニットテスト編
なべ
 
PDF
Spring bootでweb 基本編
なべ
 
PDF
Lombokのススメ
なべ
 
PPTX
はじめてのSpring Boot
なべ
 
Javaで学ぶネットワークプログラミングの基礎
なべ
 
Reladomoを使ったトランザクション履歴管理をプロダクトに適用した際のメリット/デメリット/課題など
なべ
 
普通のJavaエンジニアが、なぜ技術書を出版するに至ったか?
なべ
 
Spring bootでweb セキュリティ(ログイン認証)編
なべ
 
Spring bootでweb ユニットテスト編
なべ
 
Spring bootでweb 基本編
なべ
 
Lombokのススメ
なべ
 
はじめてのSpring Boot
なべ
 
Ad

Spring bootでweb バリデート編