Spring Boot #Valid doesn't display message from #NotBlank - spring

Why message from #NotBlank is not displayed?
Controller API:
#PostMapping("/create-folder")
public SuccessResponse createFolder(Principal principal, #Valid #RequestBody CreateFolderRequest request) {
return historyService.createFolder(principal.getName(), request.getFolderName());
}
Request Body:
#Data
public class CreateFolderRequest {
#NotBlank(message = "Folder name is mandatory.")
private String folderName;
}
JSON Response:
{
"timestamp": "2020-11-18T11:24:19.769+00:00",
"status": 400,
"error": "Bad Request",
"message": "Validation failed for object='createFolderRequest'. Error count: 1",
"path": "/api/history/create-folder"
}
Packages:
Valid:
import javax.validation.Valid;
NotBlank:
import javax.validation.constraints.NotBlank;
There is no global exception handlers in the project.

#Valid throw an exception of MethodArgumentNotValidException you massege in #NotBlank is get throw inside exception detail which isn't return to the customer. you need to extract the messages so try adding this method to the controller.
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
above code read all errors inside the exception then get thier detail (filedName - errorMessage) and put them in list and then return the list to the clinet with 400 bad request status

Related

How I can localized error messages for Spring Validation errors, like #PathVariable #Min(1), #Email etc, if I use #ControllerAdvice?

For example:
#GetMapping("/exception/{id}")
public ResponseEntity getException(#PathVariable #Min(1) Integer id) {
return ResponseEntity.ok().body(id.toString());
}
it must returns "must be greater than or equal to 1" (default message) when I call this function with id=-5 from the eng page. And something like this "doit être supérieur ou égal à 1", when i coll it from french page.
hibernate-validator jar file contains different localization messages. You need to configure based on Accept-Language header in the request it will decide which message to be displayed.
#Bean
public AcceptHeaderLocaleResolver localeResolver() {
final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setDefaultLocale(Locale.US);
return resolver;
}
The Response DTO object.
#Data
public class ErrorDto {
private LocalDateTime timestamp;
private HttpStatus status;
private String error;
public ErrorDto(HttpStatus status, String error) {
timestamp = LocalDateTime.now();
this.status = status;
this.error = error;
}
}
Then in ControllerAdvice catch ConstraintViolationException and show a response.
#ResponseStatus(code = HttpStatus.BAD_REQUEST)
#ExceptionHandler({ConstraintViolationException.class})
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
String error = "";
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
error = violation.getMessage();
}
ErrorDto errorDto = new ErrorDto(HttpStatus.BAD_REQUEST, error);
return new ResponseEntity<>(errorDto, errorDto.getStatus());
}
It will give you response for given controller.
{
"timestamp": "2022-10-23T21:08:07.0459025",
"status": "BAD_REQUEST",
"error": "doit être supérieur ou égal à 1"
}

Spring Can't recognise application.proerties file. Unable to display the configuration defines in property file

I'm trying to display my custom message while handling the exception. But I'm getting no response in the Postman. Here is code snippet that I've used
So, Here is my controller class method
#GetMapping(value="/customers/{customerId}")
public ResponseEntity<Customer> getCustomerById(#PathVariable Integer customerId) throws Exception
{
try {
Customer customer = customerService.getCustomer(customerId);
ResponseEntity<Customer> response = new ResponseEntity<Customer>(customer, HttpStatus.OK);
return response;
}
catch(Exception e)
{
throw new ResponseStatusException(HttpStatus.NOT_FOUND,environment.getProperty(e.getMessage()),e);
}
}
here is my service layer method that I'm calling from getCustomerById method -
#Service(value = "CustomerService")
public class CustomerServiceImpl implements CustomerService {
#Autowired
private CustomerDao customerDao;
#Override
public Customer getCustomer(Integer customerId) throws Exception {
Customer customer = customerDao.getCustomer(customerId);
if (customer == null) {
throw new Exception("Service.CUSTOMER_UNAVAILABLE");
}
return customer;
}
here is my property file
server.port=3557
Service.CUSTOMER_ALREADY_EXIST=Customer already present.Add customer with different attributes.
Service.CUSTOMER_UNAVAILABLE=Customer Details not found. Give valid customer details.
Postman response -
{
"timestamp": "2021-05-02T10:21:22.455+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/DemoBank/customers/8"
}
That's because you throw an exception instead of returning your custom response. Spring's exception always returns 500.
Change this line:
throw new ResponseStatusException(HttpStatus.NOT_FOUND,environment.getProperty(e.getMessage()),e);
to this:
return new ResponseEntity<>(environment.getProperty(e.getMessage()), HttpStatus.NOT_FOUND);

#Valid | response very verbose, how to customize?

I have a Spring Boot microservice and I want to valide the incoming requestBody of an endpoint.
By using #Valid with #NotBlank I have noticed that the answer is very verbose and my customized error message is deep into the object; here is an example:
{
"timestamp": "2020-12-17T09:28:26.529+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotBlank.createUserRequest.username",
"NotBlank.username",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"createUserRequest.username",
"username"
],
"arguments": null,
"defaultMessage": "username",
"code": "username"
}
],
"defaultMessage": "USERNAME IS REQUIRED",
"objectName": "createUserRequest",
"field": "username",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='createUserRequest'. Error count: 1",
"path": "/api/user/create"
}
How can I customize this object returned? I would like the response to simply be something like this:
{
"timestamp": "2020-12-17T09:28:26.529+0000",
"status": 400,
"error": "Bad Request",
"message": "USERNAME IS REQUIRED"
}
Here is my code:
Request
#Data
public class CreateUserRequest {
#NotBlank(message = "username is required")
private String username;
#Size(min = 3, max = 64)
#NotBlank(message = "password is required")
private String password;
#NotBlank(message = "confirmPassword is required")
#Size(min = 3, max = 64)
private String confirmPassword;
}
Controller
#PostMapping("/create")
public ResponseEntity<User> createUser(#Valid #RequestBody CreateUserRequest request) {
User user = appService.createUserAndCart(request);
return ResponseEntity.ok(user);
}
Thank you for your experience
You can use #ControllerAdvice/#RestControllerAdvice
it allows you to handle exceptions across the whole application. You can think of it as an interceptor of exceptions thrown by methods annotated with #RequestMapping and similar.
And add a method like this,
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleException(Exception ex)
//your custom body
return new ResponseEntity<>(body, HttpStatus.XXXXX);
}
You can specify a specific Exception type (I think it's InvalidArgumentException in your case)
Define a return class
import org.springframework.http.HttpStatus;
import java.util.HashMap;
/**
* #description:
* #author: 582895699#qq.com
* #time: 2020/12/20 下午 01:50
*/
public class Resp extends HashMap {
private static final long serialVersionUID = 1L;
public static final String TIMESTAMP = "timestamp";
public static final String STATUS = "status";
public static final String ERROR = "error";
public static final String MESSAGE = "message";
public static Resp fail(String message) {
Resp resp = new Resp();
resp.put(TIMESTAMP, System.currentTimeMillis());
resp.put(STATUS, HttpStatus.BAD_REQUEST.value());
resp.put(ERROR, HttpStatus.BAD_REQUEST.getReasonPhrase());
resp.put(MESSAGE, message);
return resp;
}
#Override
public Object put(Object key, Object value) {
return super.put(key, value);
}
}
Define global exception handling class and obtain exception information
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* #description:
* #author: 582895699#qq.com
* #time: 2020/12/20 下午 01:55
*/
#RestControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(value = MethodArgumentNotValidException.class)
public Resp methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
ObjectError objectError = bindingResult.getAllErrors().get(0);
String message = objectError.getDefaultMessage();
return Resp.fail(message);
}
}

How to wrap Path Not Found Exception in Spring Boot Rest using ExceptionHandler?

I am using Spring Boot and Spring Rest Example. In this example, I am passing custom header, if that value is valid, endpoint gets called successfully, if custom header value is not correct then I get below response, which I want to wrap into show it to the enduser using #ControllerAdvice ExceptionHandler.
Note: I went through Spring mvc - How to map all wrong request mapping to a single method, but here in my case I am taking decision based on CustomHeader.
{
"timestamp": "2020-01-28T13:47:16.201+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/employee-data/employee-codes"
}
Controller
#Operation(summary = "Find Employee")
#ApiResponses(value = { #ApiResponse(code = 200, message = "SUCCESS"),
#ApiResponse(code = 500, message = "Internal Server Error") })
#Parameter(in = ParameterIn.HEADER, description = "X-Accept-Version", name = "X-Accept-Version",
content = #Content(schema = #Schema(type = "string", defaultValue = "v1",
allowableValues = {HeaderConst.V1}, implementation = Country.class)))
#GetMapping(value = "/employees/employee-codes", headers = "X-Accept-Version=v1")
public ResponseEntity<Employees> findEmployees(
#RequestParam(required = false) String employeeCd,
#RequestParam(required = false) String firstName,
#RequestParam(required = false) Integer lastName) {
Employees response = employeeService.getEmployees(employeeCd, firstName, lastName);
return new ResponseEntity<>(response, HttpStatus.OK);
}
I've implemented HttpMessageNotReadableException and HttpMediaTypeNotSupportedException and NoHandlerFoundException, but still not able to wrap this error.
Any suggestions?
I was able to find the solution for it.
# Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Error Handling Code:
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// custom logic here
return handleExceptionInternal(ex, error, getHeaders(), HttpStatus.BAD_REQUEST, request);
}
If you're using #ControllerAdvice,
do this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}

Response error format with manual validation in Spring Data Rest

When using Spring Data REST and JSR 303 Bean Validation I get a response like the following when there's a constraint violation:
{
"errors": [
{
"entity": "Empresa",
"property": "observacao",
"invalidValue": "",
"message": "O tamanho do campo deve ser entre 1 e 255"
}
]
}
But I'm trying to validate an object manually, and I would like to return the validation errors in the same format used by Spring Data Rest.
#DeleteMapping("/departamento/{id}")
public #ResponseBody
ResponseEntity<?> filtro(#PathVariable Long id){
Optional<Departamento> departamentoOpt = this.departamentoRepository.findById(id);
if (!departamentoOpt.isPresent()) {
return ResponseEntity.notFound().build();
}
Departamento departamento = departamentoOpt.get();
BindingResult errors = new BeanPropertyBindingResult(
departamento, "departamento");
this.validator.validate(departamento, errors, PreDeleteValidation.class);
if (errors.hasErrors()) {
// How to return a response in the same format used by SDR here?
}
return ResponseEntity.ok().build();
}
How can this be accomplished?
You can throw and Exception on validation failure and register a Spring MVC Controller Advice to catch this and transform it to something that meets your needs.
if (errors.hasErrors()) {
throw new org.springframework.web.bind.MethodArgumentNotValidException(
departamento, bindingResult)
}
The advice could look something like the below:
#ControllerAdvice
public class ErrorHandlingAdvice
{
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationError processValidationError(MethodArgumentNotValidException ex)
{
ValidationError error = new ValidationError();
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors)
{
error.addError(fieldError.getField(), fieldError.getDefaultMessage());
}
return error;
}
}
ValidationError is just a simple bean:
public class ValidationError
{
private final Map<String, List<String>> errors;
public ValidationError()
{
errors = new TreeMap<>();
}
public void addError(String field, String error)
{
if (!errors.containsKey(field))
{
errors.put(field, new ArrayList<String>());
}
errors.get(field).add(error);
}
public Map<String, List<String>> getErrors()
{
return errors;
}
}

Resources