-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Include validation errors in response when a HandlerMethodValidationException is thrown #102
Comments
I had a quick look at the source code and there doesn't seem to be a specific handler for In order to include the validation errors in the response when this exception is thrown, one would have to implement a handler similar to |
This seems like it would be a good addition to this library. How does |
@Documented
@Constraint(validatedBy = MultiPartFileValidator.class)
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidFileType {
// Default list of allowed file types
String[] value() default {
MediaType.TEXT_PLAIN_VALUE,
MediaType.APPLICATION_PDF_VALUE
};
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
} For the sake of completeness, here's the validator it calls class MultiPartFileValidator implements ConstraintValidator<ValidFileType, MultipartFile> {
private List<String> allowed;
@Override
public void initialize(ValidFileType constraintAnnotation) {
allowed = List.of(constraintAnnotation.value());
}
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
return file == null || allowed.contains(file.getContentType());
}
} The specific details of this validator aren't particularly relevant. The important point is that when there's more than one validated request part, the exception that's thrown on validation failure is a |
I've added the following custom handler for this exception and it seems to work reasonably well for my use case @Component
public class MethodValidationExceptionHandler extends AbstractApiExceptionHandler {
public MethodValidationExceptionHandler(HttpStatusMapper httpStatusMapper,
ErrorCodeMapper errorCodeMapper,
ErrorMessageMapper errorMessageMapper) {
super(httpStatusMapper, errorCodeMapper, errorMessageMapper);
}
@Override
public boolean canHandle(Throwable exception) {
return exception instanceof HandlerMethodValidationException;
}
@Override
public ApiErrorResponse handle(Throwable ex) {
var response = new ApiErrorResponse(HttpStatus.BAD_REQUEST, getErrorCode(ex), getErrorMessage(ex));
var validationException = (HandlerMethodValidationException) ex;
var errors = validationException.getAllErrors();
errors.stream()
.filter(error -> StringUtils.isNotBlank(error.getDefaultMessage()))
.forEach(error -> {
if (error instanceof FieldError fieldError) {
var apiFieldError = new ApiFieldError(
fieldError.getCode(),
fieldError.getField(),
fieldError.getDefaultMessage(),
fieldError.getRejectedValue(),
null);
response.addFieldError(apiFieldError);
} else {
var lastCode = Optional.ofNullable(error.getCodes())
.filter(codes -> codes.length > 0)
.map(codes -> codes[codes.length - 1])
.orElse(null);
var apiGlobalErrorMessage = new ApiGlobalError(lastCode, error.getDefaultMessage());
response.addGlobalError(apiGlobalErrorMessage);
}
});
return response;
}
} |
I created a PR based on the code you posted here. See #107. I did not include the filter on the non-blank default error message, because I think there is still value in having it and being able to set the message from the properties. |
Thanks for your response. The PR looks good to me. Am I right in saying that if the starter and an application both include a handler for the same exception type, then the handler in the application will take precedence over the handler in the starter? I'm assuming this is what Hopefully I can use your handler instead of mine, but I'll try it out once you've released a version that includes it. |
The |
feat: Support HandlerMethodValidationException
If one wants to replace a handler in the starter, then defining the replacement handler as a subclass of the class being replaced seems quite odd. It would be preferable if it were possible to replace a handler by implementing |
You can test it now: https://github.com/wimdeblauwe/error-handling-spring-boot-starter/releases/tag/4.5.0 |
True, but that is how the Spring mechanism works. |
In my experience if I want to replace a bean defined by Spring Boot, Spring Security, etc. then I usually just have to have implement a particular interface and/or give the bean a particular name. I don't ever recall having to extend the class of the bean I'm replacing. |
It works like a charm. Thanks very much for your help. |
I have a
@RestController
with the following endpointEventRequest
is a standard DTO with constraints such asIf
eventRequest
violates any of these constraints aMethodArgumentNotValidException
is thrown, which gets converted to a list of validation errors in the response body.If I add a validator to the
file
request part (e.g. to restrict the allowed file types)Now if validation of
eventRequest
fails, a different exceptionHandlerMethodValidationException
is thrown, but the response does not include the validation errors, e.g.HandlerMethodValidationException
has a publicgetAllErrors()
method, so it should be possible to include the errors in the response when this exception is thrown.Incidentally, I tried using a
@RequestParam
instead of@RequestPart
for thefile
request part, but the outcome is the same in both cases.The text was updated successfully, but these errors were encountered: