Error GlobalExceptionHandler, Error 401 doesn't show error details - java

I recently implemented authentication in my API Rest, but when the user tries to access to api without being auntenticatedn the exception message doesn't appear:
This is my GlobalExceptionHandler with the method exception that cover all errors in the api:
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handlerExceptionMethod(Exception ex , WebRequest
webRequest){
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
webRequest.getDescription(false));
return new ResponseEntity<ErrorDetails>(errorDetails,
HttpStatus.INTERNAL_SERVER_ERROR);
}
The Postman sofware returns an 1, I want to see the errorDetails parameters. This is my ErrorDetails class:
#Getter
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
}

I solved the problem by addding the following line of code in Security COnfig class:
.antMatchers("/error").permitAll()

Related

What type of Exception is this?

I am sending a DTO from client with a failed validation for one field. I tried to handle it in the Controller Advice, but it turns out it is not the instance of the ValidationException.class as that handler does not work. I checked it using the #ExceptionHandler(Exception.class) and it worked as expected, but I want to specify the Exception type properly.
This is the DTO:
public class BookDto {
#NotBlank(message = "Name of the book cannot be empty!")
#Size(min = 4, max = 30, message = "Name of the book must be between 4 and 30 characters
long!")
public String name;
#NotBlank(message = "Author of the book cannot be empty!")
#Size(min = 4, max = 35, message = "Author of the book must be between 5 and 35
characters long!")
public String author;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public LocalDate publicationYear;
#NotNull(message = "Pages of the book cannot be null!")
#Digits(integer = 4, fraction = 0)
public int pagesNumber;
#NotBlank(message = "Publisher of the book cannot be empty!")
#Size(min = 4, max = 30, message = "Publisher of the book must be between 4 and 30
characters long!")
public String publisher;
}
This is the ControllerAdvice:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
protected ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(ResourceWasDeletedException.class)
protected ResponseEntity<MyGlobalExceptionHandler> handleDeleteException() {
return new ResponseEntity<>(new MyGlobalExceptionHandler("This book was deleted!"),
HttpStatus.NOT_FOUND);
}
#ExceptionHandler(ValidationException.class)
public ResponseEntity<?> handleValidationException(ValidationException exception,
WebRequest webRequest) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), exception.getMessage(),
webRequest.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
/* #ExceptionHandler(Exception.class)
public ResponseEntity<?> globalExceptionHandler(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}*/
#Data
#AllArgsConstructor
private static class MyGlobalExceptionHandler {
private String message;
}
}
This is the post method:
#PostMapping("/books")
#ResponseStatus(HttpStatus.CREATED)
public BookDto createBook(#Valid #RequestBody BookDto requestForSave){
var book = converter.getMapperFacade().map(requestForSave, Book.class);
log.debug("Book={}", book);
var bookDto = converter.toDto((Book) bookService.create(book));
log.debug("Book={}", bookDto);
return bookDto;
}
When I commented the last Exception handler Postman receives a response with this:
So the ValidationException handler did not work. What type of the Exception is this?
You can figure that out yourself: Restore your commented-out catch-all exception handler and just print or otherwise report ex.getClass(). exceptions are objects same as any other. They have a type, you can instanceof them, you can call .getClass() on them, you can pass them around like any other.

Spring REST API multiple RequestParams vs controller implementation

I'm wondering about proper way of implementating of controller in case of GET request with multiple request params given. In my understanding of REST it's much better to have one endpoint with additional parameters for filtering/sorting than several endpoints (one for each case). I'm just wondering about maintanance and extensibility of such endpoint. Please have a look on example below :
#RestController
#RequestMapping("/customers")
public class CustomerController {
#Autowired
private CustomerRepository customerRepo;
#GetMapping
public Page<Customer> findCustomersByFirstName(
#RequestParam("firstName") String firstName,
#RequestParam("lastName") String lastName,
#RequestParam("status") Status status, Pageable pageable) {
if (firstName != null) {
if (lastName != null) {
if (status != null) {
return customerRepo.findByFirstNameAndLastNameAndStatus(
firstName, lastName, status, pageable);
} else {
return customerRepo.findByFirstNameAndLastName(
firstName, lastName, pageable);
}
} else {
// other combinations omitted for sanity
}
} else {
// other combinations omitted for sanity
}
}
}
Such endpoint seems to be very convenient to use (order of parameters doesn't matter, all of them are optional...), but maintaining something like this looks like a hell (number of combinations can be enormous).
My question is - what is the best way to deal with something like this? How is it designed in "professional" APIs?
What is the best way to deal with something like this?
The best way to deal with it is to use the tools already available. As you are using Spring Boot and, I assume therefore, Spring Data JPA then enable the QueryDsl suppport and web support extensions for Spring Data JPA.
You controller then simply becomes:
#GetMapping
public Page<Customer> searchCustomers(
#QuerydslPredicate(root = Customer.class) Predicate predicate, Pageable pageable) {
return customerRepo.findBy(predicate, pageable);
}
and your repository is simply extended to support QueryDsl:
public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long>,
QueryDslPredicateExecutor<Customer>{
}
You can now query by any combination of params without writing any further code.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.type-safe
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.extensions.querydsl
Good day. I can't call myself a professional, but here are some tips which can make this controller looks better.
Use DTO instead of using a group of parameters
public class CustomerDTO {
private String firstName;
private String lastName;
private String status;
}
With this class your method's signature will look like this:
#GetMapping
public Page<Customer> findCustomersByFirstName(CustomerDTO customerDTO, Pageable pageable) {
...
}
Use validation if you need one
For example, you can make some of these fields are required:
public class CustomerDTO {
#NotNull(message = "First name is required")
private String firstName;
private String lastName;
private String status;
}
Don't forget to add #Valid annotation before the DTO parameter in your controller.
Use specification instead of this block with if-else
Here is a great guide on how to do it - REST Query Language with Spring Data JPA Specifications
Use the service layer, don't need to call repository from the controller
#GetMapping
public Page<Customer> findCustomersByFirstName(#Valid CustomerDTO customerDTO, BindingResult bindingResult, Pageable pageable) {
if (bindingResult.hasErrors()) {
// error handling
}
return customerService.findAllBySpecification(new CustomerSpecification(customerDTO));
}
Your controller should not contain any logic about working with entities or some business stuff. It's only about handling request/errors, redirects, views, etc...
Its good to have a POST request with such validations instead of a GET request.You can use following method for the controller.
#PostMapping(value = "/findCustomer",produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> findCustomersByFirstName(#Valid #RequestBody Customer customer){
return customerRepo.findByFirstNameAndLastNameAndStatus(customer.getFirstName, customer.getLastName(), customer.getStatus(), pageable);
}
use the DTO as follows.
public class Customer {
private String firstName;
private String lastName;
private String status;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName= firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName= lastName;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status= status;
}
public LivenessInputModel(String firstName, String lastName, String status) {
this.firstName= firstName;
this.lastName= lastName;
this.status= status;
}
public LivenessInputModel() {
}
}
And add a controller level exception advice to return the response in errors.
#ControllerAdvice
public class ControllerExceptionAdvice {
private static final String EXCEPTION_TRACE = "Exception Trace:";
private static Logger log = LoggerFactory.getLogger(ControllerExceptionAdvice.class);
public ControllerExceptionAdvice() {
super();
}
#ExceptionHandler({ BaseException.class })
public ResponseEntity<String> handleResourceException(BaseException e, HttpServletRequest request,
HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(e);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, e.getHttpStatus());
}
#ExceptionHandler({ Exception.class })
public ResponseEntity<String> handleException(Exception e, HttpServletRequest request,
HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(httpStatus.value(),
ExceptionMessages.INTERNAL_DEFAULT);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, httpStatus);
}
#ExceptionHandler({ MethodArgumentNotValidException.class })
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException e,
HttpServletRequest request, HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
ValidationException validationEx = new ValidationException(e);
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(validationEx);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, validationEx.getHttpStatus());
}
#ExceptionHandler({ HttpMediaTypeNotSupportedException.class, InvalidMimeTypeException.class,
InvalidMediaTypeException.class, HttpMessageNotReadableException.class })
public ResponseEntity<String> handleMediaTypeNotSupportException(Exception e, HttpServletRequest request,
HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(httpStatus.value(),
ExceptionMessages.BAD_REQUEST_DEFAULT);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, httpStatus);
}
#ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
public ResponseEntity<String> handleMethodNotSupportException(Exception e, HttpServletRequest request,
HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.METHOD_NOT_ALLOWED;
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(httpStatus.value(),
ExceptionMessages.METHOD_NOT_ALLOWED);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, httpStatus);
}
#ExceptionHandler({ MissingServletRequestParameterException.class })
public ResponseEntity<String> handleMissingServletRequestParameterException(Exception e, HttpServletRequest request,
HttpServletResponse response) {
log.error(EXCEPTION_TRACE, e);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
BaseExceptionResponse exceptionDto = new BaseExceptionResponse(httpStatus.value(),
ExceptionMessages.BAD_REQUEST_DEFAULT);
return new ResponseEntity<>(exceptionDto.toString(), responseHeaders, httpStatus);
}
}
Actually, you answered half of the answer yourself, Query Params are used for filtration purposes, and as you can see in your code this will be allowed via GET request. But your question regarding validations is something a trade-off.
For example; if you don't want to have this kind of check, you can depend on mandatory required = true which is the default in #RequestParam, and handle it in the response immediately.
Or you can use alternatively #RequestBody with support of #Valid for more clear info for what wrong had occurred; for example
#PostMapping(value = "/order")
public ResponseEntity<?> submitRequest(#RequestBody #Valid OrderRequest requestBody,
Errors errors) throws Exception {
if (errors.hasErrors())
throw new BusinessException("ERR-0000", "", HttpStatus.NOT_ACCEPTABLE);
return new ResponseEntity<>(sendOrder(requestBody), HttpStatus.CREATED);
}
// Your Pojo
public class OrderRequest {
#NotNull(message = "channel is required")
private String channel;
#NotNull(message = "Party ID is required")
private long partyId;
}
For more information check this #Valid usage in Spring
This way will decouple your validation mechanism from Controller layer to Business layer. which in turns will save lots of boilerplate code, but as you noticed with change to POST instead.
So in general, there is no direct answer to your question, and the short answer is it depends, so choose whatever easy for you with good capabilities and less maintainance will be the best practice
As an alternative solution besides other ones, you can use JpaSpecificationExecutor<T> in your repository and create a specification object based on your arguments and pass it to the findAll method.
So, your repository should extend from the JpaSpecificationExecutor<Customer> interface as follows:
#Repository
interface CustomerRepository extends JpaSpecificationExecutor<Customer> {
}
Your controller should get the required arguments as Map<String, String to gain dynamic behavior.
#RestController
#RequestMapping("/customers")
public class CustomerController {
private final CustomerRepository repository;
#Autowired
public CustomerController(CustomerRepository repository) {
this.repository = repository;
}
#GetMapping
public Page<Customer> findAll(#RequestBody HashMap<String, String> filters, Pageable pageable) {
return repository.findAll(QueryUtils.toSpecification(filters), pageable);
}
}
And, you should define a method to convert provided arguments to Specification<Customer>:
class QueryUtils {
public static Specification<Customer> toSpecification(Map<String, String> filters) {
Specification<Customer> conditions = null;
for (Map.Entry<String, String> entry : filters.entrySet()) {
Specification<Customer> condition = Specification.where((root, query, cb) -> cb.equal(root.get(entry.getKey()), entry.getValue()));
if (conditions == null) {
conditions = condition;
} else {
conditions = conditions.and(condition);
}
}
return conditions;
}
}
Also, You can use the Meta model to make better criteria query and combine it with the provided solution.

Creating Custom Exception for Resource Not Found 404 SPRING BOOT

I do have a class CustomResponseEntityExceptionHandler class extending ResponseEntityExceptionHandler with 2 methods one for handling wrong formats and the other one when a resource (url) is requested but does not exist. I would like to give the user a custom message instead of the default White Label Error page of Spring. I'm trying to understand why my handleResourceNotFoundException method from CustomResponseEntityExceptionHandler is not called when a non existing URI is requested but instead I keep having the White Label Error page. Thanks for your help!
curl localhost:8080/doesnotexist
This is my CustomResponseEntityExceptionHandler
#ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorDetails> handleAllWrongFormatExceptions(WrongFormatException ex,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(true));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(ResourceNotFoundException.class)
public final ResponseEntity<ErrorDetails> handleResourceNotFoundException(ResourceNotFoundException ex,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
Simple class for holding custom error details
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
public Date getTimestamp() {
return timestamp;
}
public String getMessage() {
return message;
}
public String getDetails() {
return details;
} }
And here is my controller
#Controller
public class StringManipController {
#Autowired
private StringProcessingService stringProcessingService;
#GetMapping("/")
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseBody
public String home(#RequestParam(name = "value", required = false, defaultValue = "") String value) {
return "Welcome to the String Processing Application";
}
#GetMapping("/stringDedup")
#ResponseBody
public ProcessedString doManip(#RequestParam(name = "value", required = false, defaultValue = "") String value) {
String result = stringProcessingService.getStringManipulation(value);
return new ProcessedString(result);
}
#GetMapping("/writeNumber")
#ResponseBody
public ProcessedString getWriteNumber(
#RequestParam(name = "value", required = false, defaultValue = "") String value) {
String number = stringProcessingService.getNumberToWords(value);
return new ProcessedString(number);
} }
And here the ResourceNotFoundException class
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 266853955330077478L;
public ResourceNotFoundException(String exception) {
super(exception);
} }
If you used Spring Boot:
#ControllerAdvice
public class CustomResponseEntityExceptionHandler {
#ExceptionHandler(value = { NoHandlerFoundException.class })
public ResponseEntity<Object> noHandlerFoundException(Exception ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("test");
}
}
or
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(
NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, "Test", headers, status, request);
}
}
In the org.springframework.web.servlet.DispatcherServlet class there is a variable called throwExceptionIfNoHandlerFound. If this is set to true a method called noHandlerFound will throw a NoHandlerFoundException. Your exception handler will now catch it.
Add this property to your application.properties file: spring.mvc.throw-exception-if-no-handler-found=true

How to return 400 HTTP error code when some property of a RequestBody parameter is null?

I have the following example:
This is the request body:
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
//... getter and setters
}
This is the Controller:
#RequestMapping(value = {"/login"}, method = RequestMethod.POST)
#ResponseBody
public LoginResponse login(#RequestBody(required = true) UserLoginData loginData){
//... some code
}
This is how I invoke the service:
POST /login
{"username":"neuquino"}
I expect that Spring returns a HTTP 400 BAD REQUEST error, because password is missing. But instead of that, it returns a HTTP 500 INTERNAL SERVER error with the following stacktrace:
java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:948) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
...
How can I specify to Spring that username and password are required fields in request body?
#Bart's answer was very useful to find my final solution:
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotBlank
private String username;
#NotNull
#NotBlank
private String password;
//... getter and setters
}
On my Controller I have:
public LoginResponse login(
#RequestBody(required = true) #Valid UserLoginData loginData){
//... login code
}
Until here is very similar, but it is clearer because the controller's method does not have the error validation. Instead of that, I used another class with the ControllerAdvice annotation
#ControllerAdvice
public class RestErrorHandler {
private MessageSource messageSource;
#Autowired
public RestErrorHandler(#Qualifier("messageSource") MessageSource messageSource) {
this.messageSource = messageSource;
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationError processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return this.processFieldErrors(fieldErrors);
}
private ValidationError processFieldErrors(List<FieldError> fieldErrors) {
ValidationError dto = new ValidationError();
for (FieldError fieldError : fieldErrors) {
String localizedErrorMessage = this.resolveLocalizedErrorMessage(fieldError);
dto.getErrors().put(fieldError.getField(), localizedErrorMessage);
}
return dto;
}
private String resolveLocalizedErrorMessage(FieldError fieldError) {
Locale currentLocale = LocaleContextHolder.getLocale();
String localizedErrorMessage = this.messageSource.getMessage(fieldError, currentLocale);
return localizedErrorMessage;
}
}
Now my service is returning this:
{
"errors":{
"country":"country cannot be null"
}
}
I hope it helps someone else.
To get this solution I also used what is written in this post.
If the password is missing it will not be set when the UserLoginData object is created. It will not check if the value is valid or anything. If you need to validate your login data use proper validation.
You could use the annotations in the hibernate validator package for declarative validation e.g.
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotBlank
private String username;
#NotNull
#NotBlank
private String password;
//... getter and setters
}
Your method could then be written as (note the #Valid annotation):
public LoginResponse login(
#RequestBody(required = true) #Valid UserLoginData loginData,
BindingResult result,
HttpServletResponse response){
if (result.hasErrors()) {
// Validation problems!
response.sendError(400, "Bad login data");
}
}

Capturing Hibernate Validator message text in Spring MVC

I am working on a REST api using Spring-MVC and json. I running my automatic tests using Jetty and an in-memory database. I want to test that posting an invalid domain object gives me the error message from the #NotEmpty annotation. But all I get is the default Jetty 400 Bad Request page.
I have a domain class with some validation:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "Name is a required field")
private String name;
/* getters, setters */
}
Here's the controller
#Controller
public class CompanyController {
#RequestMapping(value = "/company",
method = RequestMethod.POST,
consumes = "application/json")
public void createCompany(
#Valid #RequestBody final Company company,
final HttpServletResponse response) {
//persist company
response.setStatus(HttpServletResponse.SC_CREATED);
response.setHeader("location", "/company/" + company.getId());
}
}
How can I get the value "Name is a required field" returned as part of the response?
This does it:
#ExceptionHandler(value = MethodArgumentNotValidException.class)
public void exceptionHandler(final MethodArgumentNotValidException ex,
final HttpServletResponse response) throws IOException {
for (final ObjectError objectError : ex.getBindingResult().getAllErrors()) {
response.getWriter().append(objectError.getDefaultMessage()).append(".");
}
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
Spring 3.2 variant using ControllerAdvice and ResponseEntityExceptionHandler:
#ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object>
handleMethodArgumentNotValid(MethodArgumentNotValidException e,
HttpHeaders headers, HttpStatus status,
WebRequest request) {
BindingResult bindingResult = e.getBindingResult();
ArrayList<String> errors = new ArrayList<String>();
for (ObjectError error : bindingResult.getGlobalErrors()) {
errors.add(error.getDefaultMessage());
}
for (FieldError error : bindingResult.getFieldErrors()) {
errors.add(
String.format("%s %s", error.getField(), error.getDefaultMessage())
);
}
return handleExceptionInternal(e, StringUtils.join(errors, ", "),
headers, HttpStatus.UNPROCESSABLE_ENTITY, request);
}
You need to add a BindingResult to your handler method. That object will automatically add the results to the Model, which can then be easily accessed from the Spring Forms taglib.

Categories

Resources