I am wondering how to bind exception handling method to url mapping method:
#Controller
public class UserController {
#Autowired
UserDao userDao;
#RequestMapping(value = "/user", method = RequestMethod.GET)
public String users(#ModelAttribute("model") ModelMap model) {
model.addAttribute("userList", userDao.getAll());
String[] b = new String[0];
String a = b[1];
return "user";
}
#ExceptionHandler(Exception.class)
public String handleAllException(Exception ex, #ModelAttribute("model") ModelMap model) {
model.addAttribute("error", "Exception happened");
return "error_screen";
}
}
I intentionally provoke java.lang.ArrayIndexOutOfBoundsException in users method. But I see that handleAllException method wasn't executed.
Question:
What have I forgotten to get done to make Exception Handling work appropriately?
Try to do somedthing like this:
#ExceptionHandler(Exception.class)
public ModelAndView handleAllException(Exception ex) {
ModelAndView model = new ModelAndView("error_screen");
model.addAttribute("error", "Exception happened");
return model;
}
Try the following
#ExceptionHandler(Exception.class) -> #ExceptionHandler({Exception.class})
The reason is it has failed with the below exception while trying to invoke the handleAllException() method:
DEBUG [http-nio-8080-exec-6] --- ExceptionHandlerExceptionResolver: Failed to invoke #ExceptionHandler method: public java.lang.String controllers.handleAllException(java.lang.Exception,org.springframework.ui.ModelMap)
java.lang.IllegalStateException: No suitable resolver for argument [1] [type=org.springframework.ui.ModelMap]
HandlerMethod details:
Change the Method as below:
#ExceptionHandler(Exception.class)
public String handleAllException(Exception ex) {
// model.addAttribute("error", String.format("Exception happened"));
return "error_screen";
}
Related
I've a problem with the ExceptionHandler of Java Spring. I have a my Exception called EntityNotFoundException, and I want call ExceptionHandler method from REST Controller when exception is thrown.
This is my REST Controller method code:
#ExceptionHandler(Exception.class)
public ResponseEntity insertTicket(#Valid #RequestBody Ticket ticket, #AuthenticationPrincipal Principal principal) throws EntityNotFoundException {
ticket.setCreationTimestamp(Instant.now());
ticket.setSource(TicketSource.CLIENT);
ticket.setCurrentTicketStatus(TicketStatus.VALIDATION);
User customer = userController.findUserByUsername(principal.getName());
ticket.setCustomer(customer);
try {
ticket.setAttachments(savedFiles(
ticket.getAttachments(),
ticket.getCustomer().getUsername()
));
} catch (FileUploadException e) {
return CommonResponseEntity.NotFoundResponseEntity("ENTITY_NOT_FOUND");
}
ticketController.insertTicket(ticket);
mailSenderController.sendMail(customer.getEmail(), "TICKET_OPENED");
return CommonResponseEntity.CreatedResponseEntity("CREATED");
}
This is my Exception Handler code:
#EnableWebMvc
#ControllerAdvice
#RestControllerAdvice
public class InterceptedResponseEntityExceptionHandler extends
ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
BindingResult bindingResult = ex.getBindingResult();
List<MethodArgumentFieldError> methodArgumentFieldErrors = bindingResult
.getFieldErrors()
.stream()
.map(fieldError -> new MethodArgumentFieldError(fieldError.getField(), fieldError.getCode(), fieldError.getRejectedValue()))
.collect(Collectors.toList());
List<MethodArgumentGlobalError> methodArgumentGlobalErrors = bindingResult
.getGlobalErrors()
.stream()
.map(globalError -> new MethodArgumentGlobalError(globalError.getCode()))
.collect(Collectors.toList());
MethodArgumentError methodArgumentError = new MethodArgumentError(methodArgumentFieldErrors, methodArgumentGlobalErrors);
return new ResponseEntity<>(methodArgumentError, HttpStatus.UNPROCESSABLE_ENTITY);
}
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
MissingParameterError missingParameterError = new MissingParameterError(ex.getParameterName(), ex.getMessage());
return new ResponseEntity<>(missingParameterError, HttpStatus.UNPROCESSABLE_ENTITY);
}
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleNotFound(Exception ex, WebRequest request) {
System.out.println("inside!");
if( ex instanceof DataIntegrityViolationException){
System.out.println("Data integrity violation");
String constraintViolationErrors = ex.getMessage();
String msgErr = (constraintViolationErrors.substring(constraintViolationErrors.indexOf("=") + 1));
return new ResponseEntity<>(msgErr, HttpStatus.BAD_REQUEST);
}
if(ex instanceof UsernameNotFoundException) {
String msgErr = ex.getMessage();
return new ResponseEntity<>(msgErr, HttpStatus.BAD_REQUEST);
}
if (ex instanceof NotFoundEntityException || ex instanceof EntityNotFoundException || ex instanceof NoSuchElementException){
//return CommonResponseEntity.NotFoundResponseEntity(ex.getMessage());
System.out.println("inside the handler!");
return new ResponseEntity<>(ex.getMessage(),HttpStatus.NOT_FOUND);
}
if(ex instanceof UpdateException){
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
return null;
}
#Data
#AllArgsConstructor
public class MethodArgumentError {
private List<MethodArgumentFieldError> fieldErrors;
private List<MethodArgumentGlobalError> globalErrors;
}
#Data
#AllArgsConstructor
public class MethodArgumentFieldError {
private String field;
private String code;
private Object rejectedValue;
}
#Data
#AllArgsConstructor
public class MethodArgumentGlobalError {
private String code;
}
#Data
#AllArgsConstructor
public class MissingParameterError {
private String parameterName;
private String message;
}
#Data
#AllArgsConstructor
public class ConstraintViolationError {
private String invalidValue;
private String message;
}
}
I don't know why, when I get a DataIntegrityViolationException the ExceptionHandler is called, instead when I get an EntitynotFoundException I get this message:
java.lang.IllegalStateException: Could not resolve method parameter at index 0 in public org.springframework.http.ResponseEntity com.isssr.ticketing_system.rest.TicketRest.insertTicket(com.isssr.ticketing_system.entity.Ticket,java.security.Principal) throws com.isssr.ticketing_system.exception.EntityNotFoundException: No suitable resolver for argument 0 of type 'com.isssr.ticketing_system.entity.Ticket'
What's the problem??
I saw other strage things; I get this message:
Failed to invoke #ExceptionHandler method: public org.springframework.http.ResponseEntity com.isssr.ticketing_system.rest.TicketRest.insertTicket(com.isssr.ticketing_system.entity.Ticket,java.security.Principal) throws com.isssr.ticketing_system.exception.EntityNotFoundException
So it seems that Spring is trying to invoke another method instead of method of my ExceptionHandler.
How is possibile this?
I have created a Spring Restful Service and Spring MVC application.
Restful Service ::
Restful service returns an entity if its existing in DB. If it doesn't exist It returns a custom Exception information in ResponseEntity object.
It is working as expected tested using Postman.
#GetMapping(value = "/validate/{itemId}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public ResponseEntity<MyItem> validateItem(#PathVariable Long itemId, #RequestHeader HttpHeaders httpHeaders) {
MyItem myItem = myitemService.validateMyItem(itemId);
ResponseEntity<MyItem> responseEntity = null;
if (myItem == null) {
throw new ItemNotFoundException("Item Not Found!!!!");
}
responseEntity = new ResponseEntity<MyItem>(myItem, headers, HttpStatus.OK);
return responseEntity;
}
If the requested Entity does not exist Restful Service returns below.
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<ExceptionResponse>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
But when I am calling the above service from a spring MVC application using RestTemplate, It is returning a valid object if it exists.
If the requested object does not exist Restful service is returning the exception information but its not reaching the calling(spring MVC) application.
Spring MVC application calls Restful Web Service using Rest template
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
This is expected behavior. Rest template throws exception when the http status is client error or server error and returns the response when http status is not error status.
You have to provide implementation to use your error handler, map the response to response entity and throw the exception.
Create new error exception class with ResponseEntity field.
public class ResponseEntityErrorException extends RuntimeException {
private ResponseEntity<ErrorResponse> errorResponse;
public ResponseEntityErrorException(ResponseEntity<ErrorResponse> errorResponse) {
this.errorResponse = errorResponse;
}
public ResponseEntity<ErrorResponse> getErrorResponse() {
return errorResponse;
}
}
Custom error handler which maps the error response back to ResponseEntity.
public class ResponseEntityErrorHandler implements ResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters;
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return hasError(response.getStatusCode());
}
protected boolean hasError(HttpStatus statusCode) {
return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpMessageConverterExtractor<ExceptionResponse> errorMessageExtractor =
new HttpMessageConverterExtractor(ExceptionResponse.class, messageConverters);
ExceptionResponse errorObject = errorMessageExtractor.extractData(response);
throw new ResponseEntityErrorException(ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(errorObject));
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
}
RestTemplate Configuration - You have to set RestTemplate's errorHandler to ResponseEntityErrorHandler.
#Configuration
public class RestTemplateConfiguration {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntityErrorHandler errorHandler = new ResponseEntityErrorHandler();
errorHandler.setMessageConverters(restTemplate.getMessageConverters());
restTemplate.setErrorHandler(errorHandler);
return restTemplate;
}
}
Calling Method
#Autowired restTemplate
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
try {
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
} catch (ResponseEntityErrorException re) {
ResponseEntity<ErrorResponse> errorResponse = re.getErrorResponse();
}
Try using the #ResponseBody annotation on your Exceptionhandler. e.g:
public #ResponseBody ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {... }
You should use Custom Exception Handler to fix your case. It looks like this
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public CustomResponseEntityExceptionHandler() {
super();
}
// 404
#ExceptionHandler(value = { EntityNotFoundException.class, ResourceNotFoundException.class })
protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {
BaseResponse responseError = new BaseResponse(HttpStatus.NOT_FOUND.value(),HttpStatus.NOT_FOUND.name(),
Constants.HttpStatusMsg.ERROR_NOT_FOUND);
logger.error(ex.getMessage());
return handleExceptionInternal(ex, responseError, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}
And your code should throw some exception, eg:
if (your_entity == null) {
throw new EntityNotFoundException("said something");
}
If you get this case in somewhere else again, you just throw exception like above. Your handler will take care the rest stuffs.
Hope this help.
I've started your application and works just fine.
Maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
The controller class is :
#Controller
public class ValidationController {
#GetMapping(value = "/validate/{itemId}")
public #ResponseBody ResponseEntity<MyItem> validateItem(#PathVariable Long itemId) {
if (itemId.equals(Long.valueOf(1))) {
throw new ItemNotFoundException();
}
return new ResponseEntity<>(new MyItem(), HttpStatus.OK);
}
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
}
and the test:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ValidationController.class, secure = false)
public class TestValidationController {
#Autowired
private MockMvc mockMvc;
#Test
public void testExpectNotFound() throws Exception {
mockMvc.perform(get("/validate/1"))
.andExpect(status().isNotFound());
}
#Test
public void testExpectFound() throws Exception {
mockMvc.perform(get("/validate/2"))
.andExpect(status().isOk());
}
}
Are you sure the url you are trying to use with RestTemplate is correct?
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
Your get method is #GetMapping(value = "/validate/{itemId}"
If you don't have request mapping at the level of the controller the url should be:
http://localhost:8080/validate/1
Another difference is the missing #ResponseBody on your controller method.
Here's the scenario :
I created the following custom response exception, to fire the 401 Http Status :
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class HttpUnauthorizedException extends RuntimeException {
}
The controller that uses the exception :
#Controller
public UserController {
#RequestMapping(value = "api/user")
#ResponseBody
public String doLogin(
#RequestParam(value = "username", required = false) String username, #RequestParam(value = "password", required = false) String password) {
if(userLoggedIn(String username, String password)) {
return "OK";
}
else {
throw new HttpUnauthorizedException();
}
}
...
}
Now when I try to access the controller to see the 401 exception, the server fires the Http error code 500 instead. But interestingly enough, when I try with the HttpStatus.NOT_FOUND it actually works, the server fires 404. Is there something I'm missing on here?
Thanks in advance :-)
First throw new HttpUnauthorizedException();
then you can catch it at a normal controller that have #ControllerAdvice annotation
#ControllerAdvice // To Handle Exceptions
public class ExceptionController {
//// ...........
#ExceptionHandler({HttpUnauthorizedException.class})
#ResponseBody
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
Map<String, String> unauthorizedAccess(Exception e) {
Map<String, String> exception = new HashMap<String, String>();
log.error("unauthorized Access to the API: " + e.getMessage(), e);
exception.put("code", "401");
exception.put("reason", e.getMessage());
return exception;
}
}
I think code should be much simpler, maybe the answer was written with old Spring version.
In this example I've implemented method to handle exception - HttpClientErrorException.Unauthorized to cover authentication issue (401):
#ControllerAdvice
public class MyErrorsHandler extends ResponseEntityExceptionHandler
{
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
protected ResponseEntity<Object> handleAuthenticationError(RuntimeException ex, WebRequest request)
{
return handleExceptionInternal(ex,
"Cannot login, please check your inputs",
new HttpHeaders(), HttpStatus.UNAUTHORIZED, request);
}
}
Finally I get correct error to GUI
Hi I am wondering how to pass information to the exception handler. Say for example I doing validation. Whit #Valid. I can catch that specific exception but that does not tell me if was the first name or last name of the person that was wrong.
Maybe a custom exception with a error field attribute. If I do that is how to I through it?
#Valid have already thrown an exception if validation fails.
Can i check the bindingresult for error and throw my custom exception? How do solve this?
#RequestMapping(value = "/post", method = RequestMethod.POST)
public String post(#Valid Person person) {
System.out.println(person);
System.out.println(person2);
return "home";
}
#ExceptionHandler(Exception.class)
#ResponseBody
public List<FailureResult> handleException
(Exception re, HttpServletResponse response) {
FailureResult failureResult = new FailureResult();
failureResult.setName("name");
//wich feild failed the validation?
List<FailureResult> r = new ArrayList<FailureResult>();
r.add(failureResult);
return r;
}
I am attempting to set-up a scheme for uniformly handling exceptions in Spring. As such, I need a way to pass context information into an #ExceptionHandler-annotated method, for example consider the following:
#ExceptionHandler(Exception.class)
public void handleException(Exception ex, HttpServletRequest request) {
// Need access to myContext from login()
}
#RequestMapping(value = "{version}/login", method = RequestMethod.POST)
public void login(HttpServletRequest request, #PathVariable String version, #RequestParam("userName") String userName, #RequestParam("password") String password, ModelMap model) throws Exception {
...
myContext = "Some contextual information"
...
i_will_always_throw_an_exception();
}
Since Spring is responsible for translating a thrown exception into an invocation of handleException(), I am having difficulty trying to find a way to pass myContext to the handler. One thought I have is creating a subclass of HttpServletRequest. If that approach works I would have code like this:
#ExceptionHandler(Exception.class)
public void handleException(Exception ex, MyCustomHttpServletRequest request) {
// I now have access to the context via the following
String myContext = request.getContext();
}
#RequestMapping(value = "{version}/login", method = RequestMethod.POST)
public void login(MyCustomHttpServletRequest request, #PathVariable String version, #RequestParam("userName") String userName, #RequestParam("password") String password, ModelMap model) throws Exception {
...
myContext = "Some contextual information"
request.setContext(myContext);
...
i_will_always_throw_an_exception();
}
But, if I follow this approach, how do I properly use my own arbitrary sub-class of HttpServletRequest to make this work?
Can't you just put it into exception (if necessary - wrapping the original exception with the new one)?
#ExceptionHandler(MyContextualException.class)
public void handleException(MyContextualException ex) {
// Need access to myContext from login()
}
#RequestMapping(value = "{version}/login", method = RequestMethod.POST)
public void login(HttpServletRequest request, #PathVariable String version, #RequestParam("userName") String userName, #RequestParam("password") String password, ModelMap model) throws Exception {
...
myContext = "Some contextual information"
...
try {
i_will_always_throw_an_exception();
} catch (Exception ex) {
throw new MyContextualException(myContext, ex);
}
}
An alternative approach is to pass the context as a request attribute:
request.setAttribute("myContext", myContext);