SpringMVCの@RequestBodyの@ValidでJSONをバリデートする

昨日から引き続きやってるの表題の件ですが、 とりあえず定義したvalidatorを使って欲しい感じのエラーコードを 受け取れるところまでは出来ました。   - Controller

@ExceptionHandler
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleMethodArgumentNotValidException(
        MethodArgumentNotValidException error) {
    logger.info("JSON Validate: " + error.getMessage());
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
        <list>
            <ref bean="jsonConverter" />
        </list>
    </property>
    <property name="webBindingInitializer">
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
            <property name="validator">
                <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
            </property>
        </bean>
    </property>
</bean>

上記のように設定することで以下のようなエラーメッセージが取れてきます。

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]]

ってことで、こいつをホゲホゲしてやれば、Formの時に↓こんな風にして取得する

if (result.hasErrors()) {
    List errorList = result.getAllErrors();
    for (ObjectError error : errorList) {
        logger.info(error.getDefaultMessage());
        for (int i = 0; i < error.getCodes().length; i++ ) {
            logger.info(error.getCodes()[i]);
        }

↓のエラーコードと合わせて共通化出来そうな予感です。

INFO : com.shinodogg.hoge.HomeController - Max.hoge.id
INFO : com.shinodogg.hoge.HomeController - Max.id
INFO : com.shinodogg.hoge.HomeController - Max.java.lang.Integer
INFO : com.shinodogg.hoge.HomeController - Max

    但し、JSONの場合、Integer型の項目に文字列を突っ込んでやると、、、 MethodArgumentNotValidExceptionではなく、HttpMessageNotReadableExceptionが返ってきちゃう件は、 解決できませんでした。   事象としては、 - Formのバリデーションの時は↓だったけど、

INFO : com.shinodogg.hoge.HomeController - typeMismatch.hoge.id
INFO : com.shinodogg.hoge.HomeController - typeMismatch.id
INFO : com.shinodogg.hoge.HomeController - typeMismatch.java.lang.Integer
INFO : com.shinodogg.hoge.HomeController - typeMismatch
  • JSONの場合は↓のようなHttpMessageNotReadableExceptionが出てしまうといった感じ。
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read JSON: Can not construct instance of java.lang.Integer from String value 'aa':
not a valid Integer value
 at [Source: org.apache.catalina.connector.CoyoteInputStream@1f82d1be; 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 'aa': not a valid Integer value
 at [Source: org.apache.catalina.connector.CoyoteInputStream@1f82d1be; line: 1, column: 2] (through reference chain: com.shinodogg.hoge.Hoge["id"])

  コレに関しては↓のJIRA上での議論みると、 Spring MVC Binding Lifecycle differs between @RequestBody arguments and plain JavaBean arguments ↓って書いてあります。

We don't support a BindingResult argument after @RequestBody indeed.
I think handling HttpMessageNotReadableException or RequestBodyNotValidException centrally
from an exception resolver should be adequate.
If there are good use cases we can consider it of course.

まぁ経緯まではアレですが、自分でExceptionの中身みてハンドリングしろやって事のようです。 どのカラムについてIntegerにStringを突っ込めねーよってのはメッセージに記載されているので、 ちょっとホゲホゲすりゃイイかって感じで。   この件についてイカしたソリューションお持ちの方は是非@shinodoggまでDMとかmention 飛ばしていただけるとありがたいですmm

 

あんまり本題と関係ないですが beans:beanって書くのがかったるくなったので、 servlet-context.xml名前空間の宣言変えました。   SpringMVCプロジェクト作ると↓のようになっていると思いますが、

xmlns="http://www.springframework.org/schema/mvc"
xmlns:beans="http://www.springframework.org/schema/beans"

↓に変えてやります。

xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns="http://www.springframework.org/schema/beans"

↓ら辺にmvc:ってつけなきゃいけなくなりますが、beans:を多発するよりスッキリかな、と。

<mvc:annotation-driven />
<mvc:resources mapping="/resources/**" location="/resources/" />