2012.02.07
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に無かったとか、
知らんがな、、ってのにイロイロぶつかりましたが、
それもまた経験、、、、と。。
明日も継続してコレ調べるので、解決したら追記するなり、別エントリ書くなりいたしやす。
ソフトバンククリエイティブ
売り上げランキング: 41068


[...] « SpringMVCでJSONを受付けた時のvalidationで困っている件 [...]
Posted at 2012.02.8 1:34 PM by SpringMVCの@RequestBodyの@ValidでJSONをバリデートする | shinodogg.com