I'm trying to catch 404 error on my Spring boot + Hibernate Server when Im going to some page that doesn't exist (for example http://localhost:8080/tes (http://localhost:8080/test is working)). I've created #ControllerAdvice class but it can't catch 404 exception (other works just fine). What is problem?
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(value = NullPointerException.class)
#ResponseBody
public String handleNullPointerException(Exception e) {
System.out.println("A null pointer exception ocurred " + e);
return "nullpointerExceptionPage";
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
#ResponseBody
public String handleAllException(Exception e) {
System.out.println("A unknow Exception Ocurred: " + e);
return "unknowExceptionPage";
}
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}
Related
I have multiple ExceptionHandler in controllerAdvice. one for TimeoutException and other for Exception classes.
When i throw new TimeoutException, it gets caught in Exception Handler not in TimeoutException Handler
below is the code:
#ControllerAdvice
public class CoreControllerAdvice {
#ExceptionHandler(value = { Exception.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
log.info("handleException", ex);
}
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(Exception ex) {
log.info("handleTimeoutException", ex);
}
}
I throw Exception as
throw new TimeoutException("test");
Can some one help, why it is not caught by TimeoutException Handler
Thanks,
The parameter in the method handleTimeoutException seems incorrect to me, instead of Exception it should be TimeoutException.
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(TimeoutException ex) {
log.info("handleTimeoutException", ex);
}
I am learning about global exception handling in spring boot. I have a designed a controller annotated with #RestController which has a controller method that throws an exception. I have designed another class named GlobalExceptionHandling annotated with #RestControllerAdvice/#ControllerAdvice. It works fine and handles the exception when annotated with #RestControllerAdvice but doesn't work as expected when annotated with #ControllerAdvice. I am sharing my code and the responses i got on postman.
DemoController:
#RestController
public class DemoController {
#RequestMapping("exception/arithmetic")
public String controllerForArithmeticException()
{
throw new ArithmeticException("Divide by zero error");
}
#RequestMapping("exception")
public String controllerForException() throws Exception
{
throw new Exception("An exception occurred");
}
}
GlobalExceptionHandler: (with #RestControllerAdvice)
#RestControllerAdvice
public class GlobalExceptionHandler{
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e)
{
return "Exception: " + e.getMessage();
}
#ExceptionHandler(value = ArithmeticException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleArithmeticException(ArithmeticException e)
{
return "ArithmeticException: " + e.getMessage();
}
}
Response on postman:
Status: 404 Bad Request
Response Body: ArithmeticException: Divide by zero error
Console: Nothing gets printed on console.
GlobalExceptionHandler: (with #ControllerAdvice)
#ControllerAdvice
public class GlobalExceptionHandler{
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e)
{
return "Exception: " + e.getMessage();
}
#ExceptionHandler(value = ArithmeticException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleArithmeticException(ArithmeticException e)
{
return "ArithmeticException: " + e.getMessage();
}
}
Response on postman:
Status: 404 Bad Request
Response Body: {
"timestamp": "2020-02-15T12:41:40.988+0000",
"status": 404,
"error": "Not Found",
"message": "Divide by zero error",
"path": "/exception/arithmetic"
}
Console: Nothing gets printed on console.
Can you explain what exactly #ResponseBody do?
validate Rest URL in spring boot.
Requirement: If I hit the wrong URL then it should throw a custom exception.
ex. Correct URL is "/fulfillment/600747l/send_to_hub" If I hit "/api/600747l/send_to_hub_1" then it should return exception like
"404:- URL not Found.".
Right now it returning "500 : -
{
"timestamp": 1531995246549,
"status": 500,
"error": "Internal Server Error",
"message": "Invalid Request URL.",
"path": "/api/600747l/send_to_hub_1"
}"
you need to write NewClass with annotation #ControllerAdvice which will redirect all exceptions to this NewClass.
example
Your Custom Exception Class:
#Data
#AllArgsConstructor
#EqualsAndHashCode(callSuper = false)
public class IOApiException extends IOException {
private ErrorReason errorReason;
public IOApiException(String message, ErrorReason errorReason) {
super(message);
this.errorReason = errorReason;
}
}
Now the CustomExceptionHandler Class -
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
Logger logger = LoggerFactory.getLogger(this.getClass());
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ExceptionHandler(value = IOApiException.class)
public GlobalErrorResponse handleException(IOApiException e) {
logger.error("UNAUTHORIZED: ", e);
return new GlobalErrorResponse("URL Not Found", HttpStatus.UNAUTHORIZED.value(), e.getErrorReason());
}
//this to handle customErrorResponseClasses
public GlobalErrorResponse getErrorResponseFromGenericException(Exception ex) {
if (ex == null) {
return handleException(new Exception("INTERNAL_SERVER_ERROR"));
}
else if (ex instanceof IOApiException) {
return handleException((IOApiException) ex);
}
}
Now Your error response class:
public class GlobalErrorResponse {
private String message;
#JsonIgnore
private int statusCode;
private ErrorReason reason;
}
ErrorReason Class
public enum ErrorReason {
INTERNAL_SERVER_ERROR,
INVALID_REQUEST_PARAMETER,
INVALID_URL
}
add and register one filter who calls the GlobalExceptionHandler in exception case like this
public class ExceptionHandlerFilter implements Filter {
private final GlobalExceptionHandler globalExceptionHandler;
public ExceptionHandlerFilter(GlobalExceptionHandler globalExceptionHandler) {
this.globalExceptionHandler = globalExceptionHandler;
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception exception) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
GlobalErrorResponse errorResponse = globalExceptionHandler.getErrorResponseFromGenericException(exception);
httpResponse.setStatus(errorResponse.getStatusCode());
response.getWriter().write(new ObjectMapper().writeValueAsString(errorResponse));
}
}
#Override
public void destroy() {
}
}
Like this you can add as many exceptions you want.. and can handle it manually.
As per your question first of all you need to define a base url(e.g.-/api) so that any url must be handled through your controller.Now after base url as shown /api/600747l/send_to_hub_1 #PathVariable int id. This circumstance is important, because Spring documentation said that if method argument annotated with #PathVariable can’t be casted to specified type (in our case to int), it will be exposed as String. Hence it can cause a TypeMismatchException.
To handle this I will use #ExceptionHandler annotation on #Controller level. Such approach suits for this situation as no one else. I just need to make 2 changes in the Controller:
1.Add MessageSource field
2.Add exception handler method
#Autowired
private MessageSource messageSource;
...
#ExceptionHandler(TypeMismatchException.class)
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorInfo handleTypeMismatchException(HttpServletRequest req, TypeMismatchException ex) {
Locale locale = LocaleContextHolder.getLocale();
String errorMessage = messageSource.getMessage("error.bad.smartphone.id", null, locale);
errorMessage += ex.getValue();
String errorURL = req.getRequestURL().toString();
return new ErrorInfo(errorURL, errorMessage);
}
...
I'm trying to manage a custom error page with my custom exception.
I have this exception
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Inesistente")
public class ResourceNotAccessibleException extends Throwable{
public ResourceNotAccessibleException(String message){
super(message);
}
}
which i want to respond with a 404 error.
Than i'm managing an error controller
#ControllerAdvice
public class ErrorController {
#ExceptionHandler({ResourceNotAccessibleException.class})
public ModelAndView getErrorPage(HttpServletRequest request, Throwable ex) {
String errorMsg = "";
int httpErrorCode = getErrorCode(request);
switch (httpErrorCode) {
case 404: {
logger.error("Status Error " + httpErrorCode , ex.getMessage());
errorMsg = messageSource.getMessage("errorMessage", new Object[] { uuid, +httpErrorCode }, locale);
break;
}
case 400: {
errorMsg = "BAD REQUEST";
break;
}
case 500: {
errorMsg = messageSource.getMessage("errorMessage", new Object[] { uuid, +httpErrorCode }, locale);
logger.error("Status Error " + httpErrorCode , ex.getMessage());
break;
}
}
ModelAndView mav = new ModelAndView();
mav.addObject("errorMsg", errorMsg);
mav.setViewName("error");
return mav;
}
Now, in my controller if i have something like
if(object==null) {
throw new ResourceNotAccessibleException("Resource does not exist");
}
I should see my error view, but i'm getting the classic white error page, in my log i see the exception being hit..
The ResourceNotAccessibleException should extend Exception or RuntimeException and not Throwable. More info
If you can't change exception type, probably you could try ExceptionHandlerExceptionResolver or this awesome post about Spring exception handling
One more thing, you probably want to add some #ResponseStatus info above getErrorPage, because you are handling this exeption and #ResponseStatus annotation above the ResourceNotAccessibleException will never trigger.
So i think something like this should work:
#ControllerAdvice
public class ErrorController {
#ResponseStatus(value= HttpStatus.NOT_FOUND) // <= important
#ExceptionHandler({ResourceNotAccessibleException.class})
public ModelAndView getErrorPage(HttpServletRequest request, Throwable ex) {
String errorMsg = "";
// ... some code here
ModelAndView mav = new ModelAndView();
mav.addObject("errorMsg", errorMsg);
mav.setViewName("error");
return mav;
}
}
public class ResourceNotAccessibleException extends Exception{ // <= important
public ResourceNotAccessibleException(String message){
super(message);
}
}
If this doesn't work, you can also try to change resource view file name to something like errorPage.jsp or errorPage.html and set it like mav.setViewName("errorPage");
You need to replace the default error pages in your web container and map a status code to a particular error page.
Here are the changes you need to make:
If it's a Jetty container, here are the changes:
#Bean
public JettyEmbeddedServletContainerFactory
containerFactory(
#Value("${server.port:8080}") final String port,
#Value("${jetty.threadPool.maxThreads:600}") final String maxThreads,
#Value("${jetty.threadPool.minThreads:10}") final String minThreads,
#Value("${jetty.threadPool.idleTimeout:5000}") final String idleTimeout) {
final JettyEmbeddedServletContainerFactory factory =
new JettyEmbeddedServletContainerFactory(Integer.valueOf(port));
...
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
"/error-info.html"));
...
return factory;
}
If it's a Tomcat container, here are the changes:
#Bean
public EmbeddedServletContainerCustomizer container() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(
ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new
ErrorPage(HttpStatus.NOT_FOUND, "/error-info.html"));
}
};
}
For your ErrorController, don't set view name. It will pick the view from the error page mapping which was set earlier.
#ControllerAdvice
public class ErrorController {
#ExceptionHandler(ResourceNotAccessibleException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public ModelAndView handleResourceNotAccessibleException(
HttpServletRequest req, ResourceNotAccessibleException ex) {
...
ModelAndView mav = new ModelAndView();
mav.addObject("errorMsg", errorMsg);
retrun mav;
}
}
Location of error-info.html or jsp under resources/static
I am calling a ReST service through RestTemplate and trying to override ResponseErrorHandler in Spring 3.2 to handle custom error codes.
CustomResponseErrroHandler
public class MyResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
boolean hasError = false;
int rawStatusCode = response.getRawStatusCode();
if (rawStatusCode != 200){
hasError = true;
}
return hasError;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
//String body = IOUtils.toString(response.getBody());
throw new CustomServiceException(response.getRawStatusCode() , "custom Error");
}
}
Spring framework invokes hasError method but not handleError, so I couldn't throw my custom exception. After delving into Spring RestTemplate source code, I realized that the code in handleResponseError method is causing the issue - It is looking for response.getStatusCode or response.getStatusText and throwing exception (as statusCode/statusText is null when Rest service throws exception) and it never calls either custom implemented or default handleError method in the next line.
Spring RestTemplate source code for handleResponse method:
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
response.getStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException e) {
// ignore
}
}
getErrorHandler().handleError(response);
}
FYI, while service throws exception, I can read rawstatuscode but not statuscode from response
How to bypass this framework code and make call my custom handler?
Thanks for your help in advance.
Following link has useful information about Exception Flow for Spring ResponseErrorHandler .
Adding code here, just in-case the blog is down:
Code for ErrorHandler:
public class MyResponseErrorHandler implements ResponseErrorHandler {
private static final Log logger = LogFactory.getLog(MyResponseErrorHandler.class);
#Override
public void handleError(ClientHttpResponse clienthttpresponse) throws IOException {
if (clienthttpresponse.getStatusCode() == HttpStatus.FORBIDDEN) {
logger.debug(HttpStatus.FORBIDDEN + " response. Throwing authentication exception");
throw new AuthenticationException();
}
}
#Override
public boolean hasError(ClientHttpResponse clienthttpresponse) throws IOException {
if (clienthttpresponse.getStatusCode() != HttpStatus.OK) {
logger.debug("Status code: " + clienthttpresponse.getStatusCode());
logger.debug("Response" + clienthttpresponse.getStatusText());
logger.debug(clienthttpresponse.getBody());
if (clienthttpresponse.getStatusCode() == HttpStatus.FORBIDDEN) {
logger.debug("Call returned a error 403 forbidden resposne ");
return true;
}
}
return false;
}
}
Code for using it in RestTemplate:
RestTemplate restclient = new RestTemplate();
restclient.setErrorHandler(new MyResponseErrorHandler());
ResponseEntity<String> responseEntity = clientRestTemplate.exchange(
URI,
HttpMethod.GET,
requestEntity,
String.class);
response = responseEntity.getBody();
I don't see your RestTemplate code, but I assume you to set your ResponseErrorHandler for RestTemplate to use like:
RestTemplate restClient = new RestTemplate();
restClient.setErrorHandler(new MyResponseErrorHandler());
The exception is indeed thrown in handleError method. You can find how to throw CustomException using CustomResponseHandler from one of my previous answers.