SpringMVCを使っていて、バリューオブジェクトにアノテーションでバリデーションの定義入れて、 @Validだけ仕掛けておけばイイってのは楽チンではイイわーとか思ってたのですが、 FormとJSONとで、ちょっとばかし挙動が違くて困っています。 #ちゃんと解決してから書けばいいのですが、明日になると自分自身、内容を覚えてられるか #微妙なのでブログに書いてしまおうかな、と…w ■ HTMLのFormを受取る時 - Controller
@RequestMapping(value = "/post", method = RequestMethod.POST) public String hogePost( @Valid Hoge hoge, // Integer:id, String:nameって感じのバリューオブジェクト BindingResult result, Locale locale, Model model) { ~略~ if (result.hasErrors()) { List<FieldError> errors = result.getFieldErrors(); for (FieldError error : errors) { logger.info(messageSource.getMessage(error, locale)); } }
@NotNull @Max(100) private Integer id; @NotNull private String name;
- Bean定義
<beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> </beans:bean> <beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <beans:property name="basename"><beans:value>classpath:messages</beans:value></beans:property> </beans:bean>
- エラーメッセージファイル(messages.properties と messages_ja_JP.properties 同内容…)
typeMismatch=Type Mismatch!! Max=MaxMaxMax!!
みたいな感じの実装と設定にして↓のように Integerのidの項目に"a"とか入れて叩いてやると、 ↓のようにタイプが違いますから~ってエラーメッセージを返してくれます。
INFO : com.shinodogg.hoge.HomeController - Type Mismatch!!
■ JSON文字列を受取る時 今回のプロジェクトはクライアントからjQueryとか使って$.ajax的な感じで、 JSONがリクエストされてきます。 Controllerの中であーだこーだするのではなく、HTMLのFormと同じように @Validってチョロっと書いてドヤ顔したいわけです。 ちょこちょこハマったりしつつも、Twitterでたまに絡ませていただいてる @keisuke69さんのブログにお世話になったりしながら、 以下のような実装にしてみました。 - Controller
@RequestMapping(value="/jsonpost", method=RequestMethod.POST, headers="Accept=application/json") public String hogeJsonPost( @RequestBody @Valid Hoge hoge, //JSONのバリデート後にバリューオブジェクトに値詰めてくれる Locale locale, Model model) { ~略~ @ExceptionHandler @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public String handleMethodArgumentNotValidException( MethodArgumentNotValidException error) { logger.info("JSON Validate: " + error.getMessage());
バリデーションエラーが起こった場合は、BindResultがホゲホゲではなくて、 バリデーションエラーExceptionをハンドリングするメソッドを作ってやるのが特徴です。
って事で↓のようなクライアントからJSONをリクエストしてみます。 - jQuery
<html> <head> http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js <script type="text/javascript"> $(function(){ $('#hoge').click( function(){ $.ajax({ type: "POST", url: "http://localhost:8080/Hoge/jsonpost", dataType: "json", data: '{"id": "123", "name": "hoge"}', success: function(data){ alert(data); }, contentType: "application/json" }); }); }); </script> </head> <body> <a href="javascript: void(0)" id="hoge">hoge</a><br /> </body> </html>
結果は上記のMax(100)が効いて↓のようなのが取得できましたよ、と。
INFO : com.shinodogg.hoge.HomeController - JSON Validate: Validation failed for argument at index 0 in method: public java.lang.String com.shinodogg.hoge.HomeController.hogeJsonPost(com.shinodogg.hoge.Hoge,java.util.Locale,org.springframework.ui.Model), with 1 error(s): [Field error in object 'hoge' on field 'id': rejected value [123]; codes [Max.hoge.id,Max.id,Max.java.lang.Integer,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [hoge.id,id]; arguments []; default message [id],100]; default message [must be less than or equal to 100]]
いやー、ご丁寧にありがとうございます、素敵なメッセージ、、、なんですが、 上記のmessage.propertiesでは Max=MaxMaxMax!! って定義してるんすよね、、と。 まぁ、イイっす。こいつはもうちょい調べればエラーメッセージをプロパティファイルの内容から 食う事が出来そうな気がしないでもないです。 が、、↓のようにidの項目に数値ではなく文字列を突っ込んでみると、、、
data: '{"id": "a", "name": "hoge"}',
typeMismatch=Type Mismatch!! が出ないのは、まぁ予想通りですが、、 ↓イヤイヤ、、ちょっとコレはキツくないすかね、、と。
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not construct instance of java.lang.Integer from String value 'a': not a valid Integer value at [Source: org.apache.catalina.connector.CoyoteInputStream@3008af78; line: 1, column: 2] (through reference chain: com.shinodogg.hoge.Hoge["id"]); nested exception is org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.lang.Integer from String value 'a': not a valid Integer value at [Source: org.apache.catalina.connector.CoyoteInputStream@3008af78; line: 1, column: 2] (through reference chain: com.shinodogg.hoge.Hoge["id"]) at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:127)
確かにJSONのコンバートで失敗するのは分かるのですが、そこをちゃんと拾って、 エラーメッセージを返して欲しかったな、と。MethodArgumentNotValidExceptionで。 まぁ、コレもなんとかなるのかもですが、StackOverFlowを見ながら トライアンドエラーしていくのは、日本人の私にはなかなかシンドイす。 (ま、英語の勉強になってるのでイイかってポジティブに考えてますが…) ここまでたどり着く間にも、 HttpMediaTypeNotSupportedExceptionとか、 Spring Modules Validation使ったら動かなかったとか、 Jackson(JSONパーサー)の設定がpom.xmlに無かったとか、 知らんがな、、ってのにイロイロぶつかりましたが、 それもまた経験、、、、と。。 明日も継続してコレ調べるので、解決したら追記するなり、別エントリ書くなりいたしやす。