狠狠撸

狠狠撸Share 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

Spring bootでweb バリデート编