Hossein Mohammadi
Hossein Mohammadi

Reputation: 301

@PathVariable validation gives 500 instead of 400

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

Answers (2)

Andrei Titov
Andrei Titov

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

agitrubard
agitrubard

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

Related Questions