SlideShare a Scribd company logo
1 of 41
Download to read offline
バリデート編
アジェンダ
 はじめに
 サンプルページ
 サンプルページの準備
 バリデート
 メッセージを変更
 カスタムバリデータ
 データベースを参照するバリデート
 ローカライズ
 まとめ
はじめに
 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

Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Ryosuke Uchitate
 

What's hot (20)

MySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システムMySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システム
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
AWSとオンプレミスを繋ぐときに知っておきたいルーティングの基礎知識(CCSI監修!)
AWSとオンプレミスを繋ぐときに知っておきたいルーティングの基礎知識(CCSI監修!)AWSとオンプレミスを繋ぐときに知っておきたいルーティングの基礎知識(CCSI監修!)
AWSとオンプレミスを繋ぐときに知っておきたいルーティングの基礎知識(CCSI監修!)
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
Azure Cosmos DB のキホンと使いドコロ
Azure Cosmos DB のキホンと使いドコロAzure Cosmos DB のキホンと使いドコロ
Azure Cosmos DB のキホンと使いドコロ
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
DynamoDBの初心者に伝えたい初めて触るときの勘所
DynamoDBの初心者に伝えたい初めて触るときの勘所DynamoDBの初心者に伝えたい初めて触るときの勘所
DynamoDBの初心者に伝えたい初めて触るときの勘所
 
PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説
 
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
今こそ知りたいSpring Web(Spring Fest 2020講演資料)今こそ知りたいSpring Web(Spring Fest 2020講演資料)
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
 
MySQL 5.7にやられないためにおぼえておいてほしいこと
MySQL 5.7にやられないためにおぼえておいてほしいことMySQL 5.7にやられないためにおぼえておいてほしいこと
MySQL 5.7にやられないためにおぼえておいてほしいこと
 
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
 
Spring Bootの本当の理解ポイント #jjug
Spring Bootの本当の理解ポイント #jjugSpring Bootの本当の理解ポイント #jjug
Spring Bootの本当の理解ポイント #jjug
 
Flutter移行の苦労と、乗り越えた先に得られたもの
Flutter移行の苦労と、乗り越えた先に得られたものFlutter移行の苦労と、乗り越えた先に得られたもの
Flutter移行の苦労と、乗り越えた先に得られたもの
 
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
 
REST API に疲れたあなたへ贈る GraphQL 入門
REST API に疲れたあなたへ贈る GraphQL 入門REST API に疲れたあなたへ贈る GraphQL 入門
REST API に疲れたあなたへ贈る GraphQL 入門
 

Similar to Spring bootでweb バリデート編

Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)
Akihiro Iwaya
 
勉強会force#2 HTML5によるモバイルアプリ開発
勉強会force#2 HTML5によるモバイルアプリ開発勉強会force#2 HTML5によるモバイルアプリ開発
勉強会force#2 HTML5によるモバイルアプリ開発
Kazuki Nakajima
 
GoogleWebsiteOptimizerの使い方:非同期タグバージョン
GoogleWebsiteOptimizerの使い方:非同期タグバージョンGoogleWebsiteOptimizerの使い方:非同期タグバージョン
GoogleWebsiteOptimizerの使い方:非同期タグバージョン
VOYAGE GROUP UIO strategies section
 
System3 search
System3 searchSystem3 search
System3 search
Jun Chiba
 
今からでも遅くない! React事始め
今からでも遅くない! React事始め今からでも遅くない! React事始め
今からでも遅くない! React事始め
ynaruta
 

Similar to Spring bootでweb バリデート編 (12)

Team Foundation Server プロセステンプレートの変更 手順書
Team Foundation Server プロセステンプレートの変更 手順書Team Foundation Server プロセステンプレートの変更 手順書
Team Foundation Server プロセステンプレートの変更 手順書
 
CSS Nite in Matsuyama vol.1 - session 4
CSS Nite in Matsuyama vol.1 - session 4 CSS Nite in Matsuyama vol.1 - session 4
CSS Nite in Matsuyama vol.1 - session 4
 
GWOの使い方~非同期タグVer~
GWOの使い方~非同期タグVer~GWOの使い方~非同期タグVer~
GWOの使い方~非同期タグVer~
 
Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)
 
勉強会force#2 HTML5によるモバイルアプリ開発
勉強会force#2 HTML5によるモバイルアプリ開発勉強会force#2 HTML5によるモバイルアプリ開発
勉強会force#2 HTML5によるモバイルアプリ開発
 
GoogleWebsiteOptimizerの使い方:非同期タグバージョン
GoogleWebsiteOptimizerの使い方:非同期タグバージョンGoogleWebsiteOptimizerの使い方:非同期タグバージョン
GoogleWebsiteOptimizerの使い方:非同期タグバージョン
 
Visualforce + jQuery
Visualforce + jQueryVisualforce + jQuery
Visualforce + jQuery
 
Paint Tool 2013-05-14
Paint Tool 2013-05-14Paint Tool 2013-05-14
Paint Tool 2013-05-14
 
System3 search
System3 searchSystem3 search
System3 search
 
a-sap06「カスタムフィールドを使いこなす」
a-sap06「カスタムフィールドを使いこなす」a-sap06「カスタムフィールドを使いこなす」
a-sap06「カスタムフィールドを使いこなす」
 
コードビュー中心で開発するDreamweaverテンプレート
コードビュー中心で開発するDreamweaverテンプレートコードビュー中心で開発するDreamweaverテンプレート
コードビュー中心で開発するDreamweaverテンプレート
 
今からでも遅くない! React事始め
今からでも遅くない! React事始め今からでも遅くない! React事始め
今からでも遅くない! React事始め
 

More from なべ

More from なべ (8)

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

Spring bootでweb バリデート編