SpringBootで作成したREST APIのリクエストに対してバリデーションチェックを行うにあたって、@Validated
と@Valid
の付与の仕方を整理するためにメモ。
動作環境
- Spring Boot : 2.6.1
- Dependenciesにspring-boot-starter-web,spring-boot-starter-validationが必要です(spring initializer)。
@PathVariable
と@RequestParam
に対してのバリデーションチェック
- クラスに対して
@Validated
を付与。 - 引数にバリデーション用のアノテーション(
@Max
等)を付与。
サンプルコード
SampleRestController.java
@Validated //ここに付与
@RestController
@RequestMapping("/validTest")
public class SampleRestController {
@GetMapping("/pathValid/{limit}")
public ResponseEntity<String> pathValid(@PathVariable("limit") @Min(1) @Max(5) Integer limit) {
return ResponseEntity.ok("path validation OK!");
}
@GetMapping("/paramValid")
public ResponseEntity<String> paramValid(@RequestParam("message") @Size(min = 1, max = 5) String message) {
return ResponseEntity.ok("param validation OK!");
}
}
動作確認
@PathVariable
正常値
> curl localhost:8080/validTest/pathValid/3
path validation OK!
異常値
> curl localhost:8080/validTest/pathValid/6
{"timestamp":"2021-12-12T12:47:06.657+00:00","status":500,"error":"Internal Server Error","path":"/validTest/pathValid/6"}
ログ
2021-12-12 21:47:06.647 ERROR 8812 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: pathValid.limit: 5 以下の値にしてください] with root cause
javax.validation.ConstraintViolationException: pathValid.limit: 5 以下の値にしてください
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:120) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.13.jar:5.3.13]
at com.example.demo.SampleRestController$$EnhancerBySpringCGLIB$$424b5c2e.pathValid(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
...
上記よりバリデーションチェックが有効になっていることがわかる。
@RequestParam
正常値
> curl localhost:8080/validTest/paramValid?message=hello
param validation OK!
異常値
> curl localhost:8080/validTest/paramValid?message=message
{"timestamp":"2021-12-12T12:48:51.798+00:00","status":500,"error":"Internal Server Error","path":"/validTest/paramValid"}
ログ
2021-12-12 21:48:51.797 ERROR 8812 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: paramValid.message: 1 から 5 の間のサイズにしてください] with root cause
javax.validation.ConstraintViolationException: paramValid.message: 1 から 5 の間のサイズにしてください
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:120) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.13.jar:5.3.13]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.13.jar:5.3.13]
at com.example.demo.SampleRestController$$EnhancerBySpringCGLIB$$424b5c2e.paramValid(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
...
@RequestBody
に対してのバリデーションチェック
- 引数に対して
@Valid
を付与。
サンプルコード
コントローラ
SampleRestController.java
@RestController
@RequestMapping("/validTest")
public class SampleRestController {
@PostMapping("/reqBodyValid")
public ResponseEntity<String> reqBodyValid(@RequestBody @Valid User user) {
return ResponseEntity.ok("RequestBody validation OK!");
}
}
バリデーション対象クラス
User.java
public class User {
@Min(1)
@Max(15)
int id;
@NotNull
@Size(min = 1, max = 20)
String name;
@Min(1)
int age;
//以下getter,setter
}
動作確認
正常値
> curl -XPOST localhost:8080/validTest/reqBodyValid -H "Content-Type: application/json" -d '{\"id\":1,\"name\":\"user\",\"age\":20}'
RequestBody validation OK!
異常値
> curl -XPOST localhost:8080/validTest/reqBodyValid -H "Content-Type: application/json" -d '{\"id\":0,\"name\":\"userrrrrrrrrrrrrrrrr\",\"age\":0}'
{"timestamp":"2021-12-12T14:38:50.137+00:00","status":400,"error":"Bad Request","path":"/validTest/reqBodyValid"}
ログ
2021-12-12 23:38:50.136 WARN 20332 --- [nio-8080-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<java.lang.String> com.example.demo.SampleRestController.reqBodyValid(com.example.demo.User) with 2 errors: [Field error in object 'user' on field 'age': rejected value [0]; codes [Min.user.age,Min.age,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.age,age]; arguments []; default message [age],1]; default message [1 以上の値にしてください]] [Field error in object 'user' on field 'id': rejected value [0]; codes [Min.user.id,Min.id,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.id,id]; arguments []; default message [id],1]; default message [1 以上の値にしてください]] ]
上記よりバリデーションチェックが有効になっていることがわかる。
おわりに
バリデーションチェックに関して検索すると、全部のせみたいな感じのサンプルコードが多く、整理してみると付け方を間違えているものがあった。