Reputation: 301
In the Spring Framework validating the request respond with error 400 (Bad Request), specially when validating the request body and the request fields decorated with the
javax.validation.constraints.*
annotations (which specified in JSR 303).
For make it more clear lets go through the example:
I have decorated the fields of the class ContactDetails
with @Email
and @NotEmpty
constraints
class ContactDetails {
@Email
String email;
@NotEmpty
String message;
}
In the controller I used @Valid
annotation to make Spring Validator validate the http request body.
@RestController
class NotificationController {
@PostMapping("/sendNotification")
public String sendNotification(@Valid @RequestBody ContactDetails contactDetails) {
...
}
}
If the validation fails, it will trigger a MethodArgumentNotValidException
. By default, Spring will translate this exception to a HTTP status 400 (Bad Request).
But for validating the request params or path variables based on Spring documentations I will decorate the controller class with @Validated
and just using javax.validation.constraints.*
annotations on the parameters and I expect the same result same as validating the request body.
@Validated
@RestController
class NotificationController {
@GetMapping("/getContactDetailsByEmail/{email}")
public ContactDetails findContactDetails(@Email String email) {
...
}
}
In contrast to request body validation a failed validation will trigger a ConstraintViolationException instead of a MethodArgumentNotValidException. Spring does not register a default exception handler for this exception, so it will by default cause a response with HTTP status 500 (Internal Server Error).
I expected to get error 400 for this scenario and I do not know if I missed any thing in my code? That would be great if any body can help me with this scenario why Spring has different approaches for validating the parameters.
Upvotes: 6
Views: 4735
Reputation: 1668
In the first case - with argument annotated with @RequestBody
- Spring uses RequestResponseBodyMethodProcessor
to validate it and throws MethodArgumentNotValidException
if validation fails, which is handled later by ResponseEntityExceptionHandler
or DefaultHandlerExceptionResolver
by translating it into 400 BAD REQUEST http response code.
In the second case Spring is using AOP for method validation with MethodValidationInterceptor
class, which throws ConstraintViolationException
if validation fails.
But, unlike the first case, Spring doesn't provide a default exception handler for ConstraintViolationException
, so it's translated into 500 http response code.
That's why developers should consider creating their own exception handlers for this kind of method-level validation.
Upvotes: 6
Reputation: 193
You can create the answer you want by using the fields in the ConstraintViolationException with the following method;
@ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handlePathVariableError(final ConstraintViolationException exception) {
log.error(exception.getMessage(), exception);
final List<SisSubError> subErrors = new ArrayList<>();
exception.getConstraintViolations().forEach(constraintViolation -> subErrors.add(generateSubError(constraintViolation)));
final SisError error = generateErrorWithSubErrors(VALIDATION_ERROR, HttpStatus.BAD_REQUEST, subErrors);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
Upvotes: 6