Display only default message of Spring boot validation - MethodArgumentNotValidException - java

How can i strip down the excessive information from MethodArgumentNotValidException and keep only the required "default message" ??
I am experimenting with Validation annotations- #NotNull, #NotBlank, and #NotEmpty
I have configured a custom error message as below:-
#NotNull(message = "Aaaah !!!! firstname cannot be empty !!!")
private String firtName;
My Exception handler is :-
#RestControllerAdvice
public class ControllerAdviceClass {
#ExceptionHandler(value = MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity handleValidationException(MethodArgumentNotValidException ex)
{
return new ResponseEntity(ex.getMessage() , HttpStatus.BAD_REQUEST);
}
}
But the exception message i see on swagger is :-
Validation failed for argument [0] in public cosmosdb.User cosmosdb.CosmosController.postResult(cosmosdb.User):
[Field error in object 'user' on field 'firstName': rejected value [null]; codes [NotNull.user.firstName,NotNull.firstName,NotNull.java.lang.String,NotNull];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.firstName,firstName]; arguments []; default message [firstName]];
default message [Aaaah !!!! firstname cannot be empty !!!]]
I want to see only the default message * [Aaaah !!!! firstname cannot be empty !!!]] * and remove the extra bunkum.

I had a similar experience with adjusting the default messages to something more meaningful.
You have to implement a javax.validation.MessageInterpolation interface. From there you'll be able to interpolate your default message.
I used this site as a reference to solve my issue. https://www.baeldung.com/spring-validation-message-interpolation

#Override
protected ResponseEntity<Object>
handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
logError(ex, HttpStatus.BAD_REQUEST);
Map<String, String> errorMap = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errorMap.put(error.getField(),error.getDefaultMessage());
});
return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST);
}

#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<Object>(ex.getFieldError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
}

Related

Java/Spring: Override default #RequestBody functionality

So I have this API:
public Map<String, Object> myFunc(#RequestBody #Valid MyPrivateEntity body) {}
Which is marked with #RequestBody and #Valid
The thing is, if I omit the body when calling this API, I get the following error message:
{
"title": "Failed to parse request",
"detail": "Required request body is missing: public com.privatePackage.misc.service.rest.MyPrivateEntity com.privatePackage.misc.service.rest.MyPrivateResource.myFunc(java.lang.String, com.privatePackage.misc.service.rest.MyPrivateEntity)",
"status": 400
}
I don't want the error message to include class names and paths, instead just "Required request body is missing".
How can I do that?
Thanks
Try this code
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST) // return 400 if validate fail
public String handleBindException(BindException e) {
// return message of first error
String errorMessage = "Request not found";
if (e.getBindingResult().hasErrors())
e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return errorMessage;
}
Or use this way
public Map<String, Object> myFunc(
#RequestBody #Valid MyPrivateEntity body,
BindingResult bindingResult) { // add this parameter
// When there is a BindingResult, the error is temporarily ignored for manual handling
// If there is an error, block it
if (bindingResult.hasErrors())
throw new Exception("...");
}
Reference:
https://www.baeldung.com/spring-boot-bean-validation
If you need more control on only this endpoint then I'll suggest to mark request body optional and check in the method if it's null then return whatever message you want to show.
#RequestBody(required = false)
Try #ControllerAdvice to customise your message.
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// paste custom hadling here
}
}
Reference:
https://ittutorialpoint.com/spring-rest-handling-empty-request-body-400-bad-request/

Spring MethodArgumentNotValidException fields are all time null

I have an entity with some constraint :
#FieldMustBeEqualToAnotherField(
field = "sentCharges.currency",
fieldToCompare = "settlementAmount.currency",
message = "Sent charges currency must be null or equal to settlement amount currency")
public class MT103ApiDTO {
....
}
One exception handler :
#RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
List<Feedback> feedbacks = new LinkedList<>();
ex.getBindingResult().getFieldErrors().forEach(fields -> {
feedbacks.add(new Feedback().setCode(INVALID_FORMAT)
.setLabel(fields.getDefaultMessage())
.setSeverity(ERROR)
.setType(BUS)
.setSource(fields.getField()));
});
return handleExceptionInternal(ex, createAndLogError(ex, feedbacks), headers, BAD_REQUEST, request);
}
}
My probem is when I throw the MethodArgumentNotValidException I catch it but on ex.getBindingResult().getFieldErrors() I have nothing, is all time empty but on stacktrace i can see the names of fields in error
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.soprabanking.ibd.digitalswiftmanager.model.api.MessageApiDTO com.soprabanking.ibd.digitalswiftmanager.resource.MT103Resource.generate(com.soprabanking.ibd.digitalswiftmanager.model.api.MT103ApiDTO): [Error in object 'MT103ApiDTO': codes [FieldMustBeNullIfAnotherFieldIsIntoList.MT103ApiDTO,FieldMustBeNullIfAnotherFieldIsIntoList]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [MT103ApiDTO.,]; arguments []; default message [],sentCharges,chargeBearer,[Ljava.lang.String;#2bd828f4]; default message [Sent charges must be null if charge bearer code is SHA or BEN]]
I dont understand where is the problem... if someone have an answer.
Thx everyone and sorry for my bad english....

How to create custom "Request Body is Missing" error in Spring Boot

To create custom error, we need to know what kind of Exception error it is. The problem is I cannot determine what kind of error the "request body is missing one" is. At first I thought it was categorized as MethodArgumentNotValidException , but it doesn't catch the error.
I create the Controller Advice for the error
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatus status, WebRequest request){
MyObject<Object> error = MyObject.failure("Invalid Parameter");
log.error("argument invalid", exception);
return new ResponseEntity<Object>(error, new HttpHeaders(), HttpStatus.OK);
}
The controller
#PostMapping(value = "/tes")
public MyObject<MyRes> myTest(#Valid #RequestBody MyReq req, HttpServletRequest hsReq) throws Exception{
return myService.updateTestData(req);
}
I used Postman to call the API.
* First Trial with bracket
* Second Trial - without bracket
No error occurred.
My question is, how to handle this error, When no request body attached at all in the request. I want to return the "invalid param" error in this case too.
This might be late but you might want to try adding this on your ControllerAdvice class, that's how I caught the Required request body is missing error when trying to send an empty request.
#ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<Object> handleMissingRequestBody(HttpMessageNotReadableException ex) {
return new ResponseEntity<Object>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}

Spring Boot error controller retrieve original request

By default Spring Boot maps /error to BasicErrorController. I want to log the exception along with the request that causes the exception. How can I get the original request in BasicErrorController or a new CustomErrorController. It seems that Spring Boot will make a new request to /error when an exception is thrown and the orginal request info is gone or no way to map the error with the original request.
Get it by:
String url = (String) request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
To avoid any misleading information, Spring Boot DOES NOT make a new request to /error endpoint. Instead, it wraps the exception in the original request and forwards it to /error endpoint. The request will be processed by BasicErrorHandler if you don't provide a custom error handler.
In this case, if you are using an interceptor, the interceptor will be invoked twice - one for the original request and the other for the forwarded request.
To retrieve the original request information, please look into the forwarded request's attributes. Basically, you can get the error message from these attributes javax.servlet.error.message, javax.servlet.error.status_code, org.springframework.web.servlet.DispatcherServlet.EXCEPTION.
And these are some resources that are related to error handling in Spring Boot:
spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
https://www.baeldung.com/exception-handling-for-rest-with-spring
https://www.baeldung.com/spring-boot-custom-error-page
If you are using controller advice to handle your exceptions then method with #ExceptionHandler can inject request as parameter, something like :
#ControllerAdvice
public class YourExceptionHandler
{
#ExceptionHandler
public ResponseEntity handleExceptions(HttpServletRequest request, Exception exception)
{
// use request to populate error object with details like requestId
LOGGER.debug(String.valueOf(request));
LOGGER.error(exception.getMessage(), exception);
}
}
Here is a working example:
#RestController
public class MyErrorController implements ErrorController {
private static final Logger LOG = LoggerFactory.getLogger(MyErrorController.class);
private static final String PATH = "/error";
private final ErrorAttributes errorAttributes;
public MyErrorController(ErrorAttributes errorAttributes) {
this.errorAttributes = errorAttributes;
}
#RequestMapping(value = PATH)
public ErrorDTO error(WebRequest webRequest, HttpServletRequest httpServletRequest) {
// Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
Map<String, Object> attrs = errorAttributes.getErrorAttributes(webRequest, ErrorAttributeOptions.defaults());
LOG.warn("Forwarded Error Request: {} ", attrs.get("path"), (Throwable)
httpServletRequest.getAttribute("javax.servlet.error.exception"));
ErrorDTO dto = new ErrorDTO();
dto.message = (String) attrs.get("error");
dto.path = (String) attrs.get("path");
dto.timestamp = attrs.get("timestamp").toString();
return dto;
}
}
#Override
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException exception,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
OriginalRequestObject originalRequest = (OriginalRequestObject) exception.getBindingResult().getTarget();
ErrorResponse errorResponse = new ErrorResponse(
status.value(),
originalRequest.getId() + " " + exception.getMessage());
return ResponseEntity.status(status).body(myErrorResponse);
}

Spring #ExceptionHandler does not return content unless body is empty

I'm using Spring #ControllerAdvice to handle exceptions
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { DataIntegrityViolationException.class})
#ResponseBody
public ResponseEntity<String> unknownException(Exception ex, WebRequest req) {
return new ResponseEntity<>(ex.getCause().getMessage(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
The problem i'm experiencing is that when the exception occurs (when i send a request via swagger), i do not get an expected exception message, but :
{"error": "no response from server"}
Response Code : 0
Response Body : No Content
I can clearly see in debug mode that the method annotated by #ExceptionHandler is called.
I've experimented with method return types, #ResponseBody, #ResponseStatus annotations and a few other thing that came to mind, but it seems that i only get some non-empty response when i return a ResponseEntity without a body, e.g.
ResponseEntity.noContent().build()
or
ResponseEntity.ok().build()
In such cases i get correct http code and a few headers
Please advise on what i'm doing wrong
Spring version 4.3.9
Spring boot version 1.5.4
Thank you in advance
UPD
I carried on experimenting and this is the solution that worked for me.
It is quite close to one of the answers - i will mark that one as accepted
In short, i just created my own dto class , populated the instance with the exception details i was interested in and returned it directly
My code
#ExceptionHandler(value = { DataIntegrityViolationException.class})
#ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ExceptionDetailHolder unknownException(Exception ex, WebRequest req) {
final Throwable cause = ex.getCause();
return new ExceptionDetailHolder("Error interacting with the database server",
cause.getClass() + ":" + cause.getMessage(),
cause.getCause().getClass() + ":" + cause.getCause().getMessage()
);
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
private class ExceptionDetailHolder {
private String message;
private String exceptionMessage;
private String innerExceptionMessage;
}
Results (which also show the contents of ex.getMessage and ex.getCause().getMessage() as asked by commenters) :
{
"message": "Error interacting with the database server",
"exceptionMessage": "class org.hibernate.exception.ConstraintViolationException:could not execute statement",
"innerExceptionMessage": "class com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Column 'allow_copay_payments' cannot be null"
}
My way of handling exception is like below, I find the specific exception and then create my own class object ValidationErrorDTO in this case, then populate required fields in that class (ValidationErrorDTO):
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ValidationErrorDTO> processValidationIllegalError(HttpMessageNotReadableException ex,
HandlerMethod handlerMethod, WebRequest webRequest) {
Throwable throwable = ex.getMostSpecificCause();
ValidationErrorDTO errorDTO = new ValidationErrorDTO();
if (throwable instanceof EnumValidationException) {
EnumValidationException exception = (EnumValidationException) ex.getMostSpecificCause();
errorDTO.setEnumName(exception.getEnumName());
errorDTO.setEnumValue(exception.getEnumValue());
errorDTO.setErrorMessage(exception.getEnumValue() + " is an invalid " + exception.getEnumName());
}
return new ResponseEntity<ValidationErrorDTO>(errorDTO, HttpStatus.BAD_REQUEST);
}

Categories

Resources