RestControllerとBean Validationでの、ちょっとした動作確認。
リクエストをマッピングするこんなクラスと
src/main/java/org/littlewings/spring/restvalidate/ParamBean.java
package org.littlewings.spring.restvalidate; import javax.validation.constraints.Digits; public class ParamBean { @Digits(integer = 5, fraction = 0) private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
こういうRestControllerを用意。
src/main/java/org/littlewings/spring/restvalidate/TestController.java
package org.littlewings.spring.restvalidate; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @RequestMapping(value = "validOnly", method = RequestMethod.POST) public String validOnly(@RequestBody @Validated ParamBean bean) { return "OK!"; } @RequestMapping(value = "validWithBindingResult", method = RequestMethod.POST) public String validWithBindingResult(@RequestBody @Validated ParamBean bean, BindingResult bindingResult) { if (!bindingResult.hasErrors()) { return "OK!"; } else { return "NG!"; } } }
両方ともリクエストのバリデーションは行いますが、片方は@Validatedアノテーションを付けるのみ、もう片方はBindingResultを受け取るように実装。
違いが出るのか?ということで、確認してみます。
@Validatedアノテーションのみの場合から。バリデーションOKの時。
$ curl -XPOST -i -H 'Content-Type: application/json' http://localhost:8080/validOnly -d '{ "value": 100 }' HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/plain;charset=UTF-8 Content-Length: 3 Date: Thu, 01 Oct 2015 15:19:50 GMT OK!
NGの時。
$ curl -XPOST -i -H 'Content-Type: application/json' http://localhost:8080/validOnly -d '{ "value": "abc" }' HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Thu, 01 Oct 2015 15:20:05 GMT Connection: close {"timestamp":1443712805359,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MethodArgumentNotValidException","message":"Validation failed for argument at index 0 in method: public java.lang.String org.littlewings.spring.restvalidate.TestController.validOnly(org.littlewings.spring.restvalidate.ParamBean), with 1 error(s): [Field error in object 'paramBean' on field 'value': rejected value [abc]; codes [Digits.paramBean.value,Digits.value,Digits.java.lang.String,Digits]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [paramBean.value,value]; arguments []; default message [value],0,5]; default message [numeric value out of bounds (<5 digits>.<0 digits> expected)]] ","path":"/validOnly"}
例外が返ってきて、HTTPステータスコードは400になりますよっと。
今度は、@Validated+BindingResultの場合。バリデーションOKの時。
$ curl -XPOST -i -H 'Content-Type: application/json' http://localhost:8080/validWithBindingResult -d '{ "value": 100 }'HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/plain;charset=UTF-8 Content-Length: 3 Date: Thu, 01 Oct 2015 15:20:51 GMT OK!
NGの時。
$ curl -XPOST -i -H 'Content-Type: application/json' http://localhost:8080/validWithBindingResult -d '{ "value": "abc" }' HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/plain;charset=UTF-8 Content-Length: 3 Date: Thu, 01 Oct 2015 15:21:30 GMT NG!
というわけで、BindingResultを受けとるようにするとメソッド内に制御が移り、なおかつHTTPステータスコードは特に何も操作しなければ200になりました。
追記)
コメントで指摘をいただきましたが、RestControllerの場合は通常@ExceptionHandlerでハンドリングするものらしいです。
Validating Spring REST controllers' beans using the Bean Validation API... and writing the tests for it! - Codes And Notes
5.16. RESTful Web Service — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.0.1.RELEASE documentation