Successfully reported this slideshow.

Spring bootでweb バリデート編

10

Share

Upcoming SlideShare
Spring bootでweb 基本編
Spring bootでweb 基本編
Loading in …3
×
1 of 41
1 of 41

More Related Content

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Spring bootでweb バリデート編

  1. 1. バリデート編
  2. 2. アジェンダ  はじめに  サンプルページ  サンプルページの準備  バリデート  メッセージを変更  カスタムバリデータ  データベースを参照するバリデート  ローカライズ  まとめ
  3. 3. はじめに  Spring Bootを使って、 Webの入力妥当性チェック(バリデート)の 実装方法を試してみる。
  4. 4. サンプルページ  サンプルページの画面フローは下記の通り 商品入力画面 確認画面 完了画面 【確認】クリック 【登録】クリック 入力エラー時 【戻る】クリック 【戻る】クリック
  5. 5. サンプルページ  サンプルページの画面フローは下記の通り 商品入力画面 確認画面 完了画面 【確認】クリック 【登録】クリック 入力エラー時 【戻る】クリック 【戻る】クリック ここの話 どのように入力チェックをするか?
  6. 6. サンプルページ  Controller /** 商品関連のページを制御するController */ @Controller @RequestMapping("product") public class ProductController { } http://someserver/someapp/product/ に 関連付けたController
  7. 7. サンプルページの準備  まずサンプルページの下記画面フローを作成する 商品入力画面 確認画面 完了画面 【確認】クリック 【登録】クリック 入力エラー時 【戻る】クリック 【戻る】クリック
  8. 8. サンプルページの準備  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 が レンダリングされる
  9. 9. サンプルページの準備  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
  10. 10. サンプルページの準備  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 の記述を追加
  11. 11. サンプルページの準備  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 が レンダリングされる
  12. 12. サンプルページの準備  Template (confirm.html) <body> <h3>商品入力 - 確認</h3> <form action="finish.html" method="post"> Code: <br/> 商品名: <br/> 金額: <br/> <input type="submit" name="naviButton" value="登録"/> </form> </body>
  13. 13. サンプルページの準備  動作確認 http://someserver/someapp/product/input http://someserver/someapp/product/confirm
  14. 14. サンプルページの準備  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/> 入力画面を実装する
  15. 15. サンプルページの準備  Form /** 画面の値を保持するForm */ public class ProductForm { private Integer productCode; private String productName; private Integer price; // getter/setteを省略 フォームクラスを実装する これが入力値の格納先となる
  16. 16. サンプルページの準備  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クラスを初期化する 引数に入れた項目が 画面上の同名の項目にマップされる
  17. 17. サンプルページの準備  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/> 確認画面を実装する
  18. 18. サンプルページの準備  動作確認 http://someserver/someapp/product/input http://someserver/someapp/product/confirm
  19. 19. バリデート  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までの入力
  20. 20. バリデート  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に対して バリデートをすることを明記 受け取ったバリデート結果を参照し、 エラーがあれば入力画面に遷移する
  21. 21. バリデート  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/> エラーがあるか? エラーがあれば、内容を出力
  22. 22. バリデート  動作確認 http://someserver/someapp/product/input 未入力、 10文字超え、 1000超え エラーメッセージを表示
  23. 23. メッセージを変更  初期状態ではメッセージが全て英語のため、 これを日本語にする
  24. 24. メッセージを変更  方法1:個別に指定する public class ProductForm { @NotNull(message="入力してください(埋め込み)") private Integer productCode; 各バリデート用のアノテーションにある messageプロパティで指定する http://someserver/someapp/product/input
  25. 25. メッセージを変更  方法2:プロパティファイルで指定する javax.validation.constraints.NotNull.message=入力してください(プロパティ) プロパティファイルに対応するアノテーションの メッセージをプロパティファイルで指定する http://someserver/someapp/product/input ValidationMessages.properties
  26. 26. メッセージを変更  可変項目をメッセージに埋め込む org.hibernate.validator.constraints.Length.message=入力は{max}文字までです。 アノテーションのパラメータ名を プロパティファイルで指定する http://someserver/someapp/product/input @NotBlank @Length(min=1, max=10) private String productName; {max}に 10が埋め込まれる
  27. 27. カスタムバリデータ  自前のバリデート機能を実装する方法は次の通り (サンプルとして電話番号のバリデータを実装する)
  28. 28. カスタムバリデータ  アノテーションを宣言する (お約束の書き方) @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 {}; } 実際にチェックを行う クラスを指定(後述) デフォルトメッセージ
  29. 29. カスタムバリデータ  チェック処理を実装する 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; } } チェック対象のアノテーションと チェック対象のデータ型 ここにチェック処理を 実装する 初期処理
  30. 30. カスタムバリデータ  チェック処理を実装する 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つあること
  31. 31. カスタムバリデータ  カスタムバリデータを利用する @NotBlank @TelNumber private String telNumber; http://someserver/someapp/product/input
  32. 32. データベースを参照するバリデート  今までのBean Validationは Controllerの手前での出来事だったが、 データベースを参照するバリデートはControllerの先の動作となる Controller Service ブラウザ Bean Validation データベースを参照する バリデート
  33. 33. データベースを参照するバリデート  Serviceにバリデート用メソッドを用意する /** Productをチェックする */ public void validate(ProductForm productForm) throws DuplicateProductException { if ( productForm.getProductCode() == 1 ) { throw new DuplicateProductException(); } } サンプルとして、商品コードに 「1」が入れられたら例外を投げる 異常時は例外を投げる
  34. 34. データベースを参照するバリデート  サービス固有の例外を作成する public class DuplicateProductException extends Exception { } Exceptionを拡張して 必ずキャッチさせる
  35. 35. データベースを参照するバリデート  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でフィールド ごとのエラーを詰める
  36. 36. データベースを参照するバリデート  メッセージ用のプロパティファイルを用意する duplicate={0}が重複しています。 「キー = メッセージ」の形式で記述 messages.properties errors.rejectValue("productCode", "duplicate", new String[]{"商品コード"}, "default message."); rejectValueしたときのerrorCodeを プロパティと同じキーにする 置換箇所「{0}」に 「商品コード」を当てる
  37. 37. データベースを参照するバリデート  アプリケーション設定を変更する spring: messages: basename: messages 先ほどのmessages.propertiesを 参照するよう設定する application.yml http://someserver/someapp/product/input
  38. 38. データベースを参照するバリデート  その他の方法 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を削除 コントローラの例外を 一手に引き受ける
  39. 39. ローカライズ  言語ごとのプロパティファイルを配置し、多言語対応をする 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 英語
  40. 40. まとめ  SpringというよりBean Validation(JSR-303,JSR-349)の仕様を 知る方が、学習の近道かもしれない・・・?  また、Springの採用している実装のHibernate Validatorも確認を
  41. 41. まとめ  参考 ■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

×