SpringMVCでJSONを受付けた時のvalidationで困っている件

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));
        }
     }

– Hoge

@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>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<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に無かったとか、
知らんがな、、ってのにイロイロぶつかりましたが、
それもまた経験、、、、と。。
 
明日も継続してコレ調べるので、解決したら追記するなり、別エントリ書くなりいたしやす。
 

Javaフレームワーク開発入門
木村 聡
ソフトバンククリエイティブ
売り上げランキング: 41068

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

コメント

  1. […] « SpringMVCでJSONを受付けた時のvalidationで困っている件 […]