Spring boot overriding default 404 exception handling - spring

I have found a lot of questions and answers around this but it seems no matter what I do I cannot bypass the default white label app page for 404 errors. Using boot version 1.4.x
What I am doing is in my application.yml:
spring:
mvc:
throw-exception-if-no-handler-found: true
Then defining my own Subclass of ResponseEntityExceptionHandler annotated with #ControllerAdvice where I stick in
my overridden handleNoHandlerFoundException
#ControllerAdvice
public class ThisIsNotWorking extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex,
final HttpHeaders headers, final HttpStatus status,
final WebRequest request) {
logger.info(ex.getClass().getName());
final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();
^^only a snippet above not all the logic.
But when I navigate to a bad route for my boot app I get json in browser with error. What else do I need to do? Ultimately I want to handle these 404s in a custom way for my app.

What I do I cannot bypass the default white label app page for 404
errors
Basically, to handle whitelabel error (404) pages, you can simply use the addErrorPages inside customize() from EmbeddedServletContainerCustomizer and handle 404 errors as shown below (you don't need #ControllerAdvice handleNoHandlerFoundException() method from your code):
#Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
"/YOUR_PAGE.html"));
}
}

Related

Spring Reactive - Exception Handling for Method Not Allowed Exception not triggering post Spring 3.0.0 & Java 17 upgrade

We recently upgraded our Spring Reactive APIs that were running on Java 11 and Spring 2.7.x. Exceptions in the Controller layer are handled by a Global Exception Handler which also handled the Method Not Supported exception. Post the upgrade, we are getting internal server error instead of Method not allowed exception when we try a different HTTP verb other that the one that a specific endpoint is designated to.
Our application has both of the below dependencies:
spring-boot-starter-web
spring-boot-starter-webflux
Searched for some stack overflow links and tried adding the below piece of code but didn't help either.
#Component
#Order(-2)
public class RestWebExceptionHandler implements ErrorWebExceptionHandler {
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof HttpRequestMethodNotSupportedException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
// marks the response as complete and forbids writing to it
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<PlanResponse> handleHttpRequestMethodNotSupportedException(
final HttpRequestMethodNotSupportedException exception) {
return responseBuilderRegistry.getResponseBuilderByType(HttpRequestMethodNotSupportedResponseBuilder.class)
.buildResponse(exception);
That was a common issue that was "recently" addressed on Spring MVC and Spring Webflux.
If you are interested in it, this issue was discussed here https://github.com/spring-projects/spring-framework/issues/22991
Just created a project to test this, can you please try the following
#RestControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MethodNotAllowedException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<Void> handle(Exception e, ServerWebExchange exchange) {
return exchange.getResponse().setComplete();
}
}

Controller Advice not handling FileTooLargeException by exceuting method annotated with FileTooLargeException

My rest controller contains following post mapping :
#PostMapping
public ResponseEntity<RespDTO> uploadDocument(#ModelAttribute #Valid RequestDTO requestDTO,#RequestParam(value = "fileContent") MultipartFile fileContent) throws ServiceException, URISyntaxException { }
ServiceExceptionn is a custom exception specific to my application.
The controller advice looks like below:
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
#Override
public ResponseEntity process(#Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
}
#ExceptionHandler(FileTooLargeException.class)
public ResponseEntity<ResponseDTO> handleFileTooLargeException(FileTooLargeException ex, #Nonnull NativeWebRequest request){
}
}
application.yml contains below property :
spring:
servlet:
multipart:
max-file-size: 2MB
If I call the rest api using file having size greater than 2MB, then I am getting below exception:
io.undertow.server.handlers.form.MultiPartParserDefinition$FileTooLargeException: UT000054: The maximum size 5242880 for an individual file in a multipart request was exceeded
Issue that I am facing here is:
the controller advice is not working as per expectation.
handleFileTooLargeException - this method must get executed because it is annotated with ExceptionHandler mentioned with the specific exception type.
But instead of that, the control goes into process method of the controller advice.
Not able to understand what I am missing here.
You need ExceptionTranslator class to extend ResponseEntityExceptionHandler class to enable methods with #ExceptionHandler annotation

Http status code from a global #ExceptionHandler with the #ControllerAdvice annotation

I'm implementing a global exception handler inside a Spring Boot App, with the #ControllerAdvice annotation, and I'd like to know, how could I get the http status code for showing a different message when it's 404 and to persist a log with the error, in other cases.
This is a simplified version of the code:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
...
ModelAndView model = new ModelAndView();
model.addObject("message", ex.getMessage());
model.addObject("trace", trace);
model.addObject("path", path);
//model.addObject("status", response.getStatus());
model.setViewName("error");
return model;
}
I've tried this approach, without success:
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
Integer statusCode = Integer.valueOf(status.toString());
To get the request attribute, this other name; javax.servlet.error.status_code doesn't work either.
You have to set your own status code corresponding every exception that you are handling. If any exception missed, default will be 5.x.x server error.
I remember doing this by extracting the expected exception to a separate class that extends Exception.
By doing this, you can add #ResponseStatus to set your required status code.
This custom exception can be thrown in your controller needed.
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Person Not Found")
public class PersonNotFoundException extends Exception {
public PersonNotFoundException (int id){
super("PersonNotFoundException with id="+id);
}
}
Instead of specifying the generic RunTime exception, handle the PersonNotFoundException in your #ExceptionHandler and add the exception object to your ModelAndView.

#RestControllerAdvice vs #ControllerAdvice

What are the major difference between #RestControllerAdvice and #ControllerAdvice ??
Is it we should always use #RestControllerAdvice for rest services and #ControllerAdvice MVC ?
#RestControllerAdvice is just a syntactic sugar for #ControllerAdvice + #ResponseBody, you can look here.
Is it we should always use #RestControllerAdvice for rest services and
#ControllerAdvice MVC?
Again, as mentioned above, #ControllerAdvice can be used even for REST web services as well, but you need to additionally use #ResponseBody.
In addition, we can just understand it as:
#RestControler = #Controller + #ResponseBody
#RestControllerAdvice = #ControllerAdvice + #ResponseBody.
Keeping in mind that #RestControllerAdvice is more convenient annotation for handling Exception with RestfulApi.
Example os usage:
#RestControllerAdvice
public class WebRestControllerAdvice {
#ExceptionHandler(CustomNotFoundException.class)
public ResponseMsg handleNotFoundException(CustomNotFoundException ex) {
ResponseMsg responseMsg = new ResponseMsg(ex.getMessage());
return responseMsg;
}
}
In that case any exception instanceOf CustomNotFoundException will be thrown in body of response.
Example extracted here:
https://grokonez.com/spring-framework/spring-mvc/use-restcontrolleradvice-new-features-spring-framework-4-3
Exception: A good REST API should handle the exception properly and send the proper response to the user. The user should not be rendered with any unhandled exception.
A REST API developer will have two requirements related to error handling.
Common place for Error handling
Similar Error Response body with a proper HTTP status code across APIs
#RestControllerAdvice is the combination of both #ControllerAdvice and #ResponseBody
The #ControllerAdvice annotation was first introduced in Spring 3.2.
We can use the #ControllerAdvice annotation for handling exceptions in the RESTful Services but we need to add #ResponseBody separately.
Note:
GlobalExceptionHandler was annotated with #ControllerAdvice, thus it is going to intercept exceptions from controllers accross the application.
The differences between #RestControllerAdvice and #ControllerAdvice is :
#RestControllerAdvice = #ControllerAdvice + #ResponseBody. - we can
use in REST web services.
#ControllerAdvice - We can use in both MVC and Rest web services, need to
provide the ResponseBody if we use this in Rest web services.
For Example :
Exception Class:
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message){
super(message);
}
}
usage of the above exception in Rest Web Service.
#RestControllerAdvice
public class MyRestControllerAdviceHandler {
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseMsg resourceNotFoundException(ResourceNotFoundException ex) {
ResponseMsg resMsg = new ResponseMsg(ex.getMessage());
return resMsg;
}
}
usage of the above exception in MVC.
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
If you use #ControllerAdvice and return your error object from a method then it will look for a view with the name of your error object so instead of returning the expected response it will return 404 for not founding a view page with that name
#ControllerAdvice
public class CustomizedExceptionHandler {
#ExceptionHandler({ UserNotFoundException.class })
#ResponseStatus(code = HttpStatus.BAD_REQUEST)
public ExceptionResponce handleUserNotException(Exception ex, WebRequest request) throws Exception {
ExceptionResponce exceptionResponce = new ExceptionResponce(new Date(), ex.getMessage(),
request.getDescription(false));
return exceptionResponce;
}
}
As in the above code, I want to return 400 (BAD_REQUEST) but
instead of 400, it is returning 404(NOT_FOUND)
You can solve this issue by using any of the below ways
add #ResponseBody to your method or class.
Use #RestControllerAdvice.
Or you can wrap your error object in ResponseEntity.
After using either of the above ways it returns the correct response

Stacktrace of exceptions in Spring Rest responses

I have few Rest web services implemented through Spring. The problem is that if any exception is thrown the webservice returns json object with formatted error message that contains stacktrace. Can I have a single point of handling exceptions, and return my custom json objects with messages that wouldn't contain stacktrace?
I see descriptions for spring mvc but im not really using that for building my views etc.
I know it's too late, but just pointing out some solutions that may help others!
case 1: if you're using application.properties file, add following line to your properties file.
server.error.include-stacktrace=on_trace_param
case 2: if you're using application.yml file, add following line to your yml file.
server:
error:
include-stacktrace: on_trace_param
case 3: In case, none of them works, try following changes:
Try to suppress the stack trace by overriding fillInStackTrace method in your exception class as below.
public class DuplicateFoundException extends RuntimeException {
#Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
ps1: I referred this article.
Spring provides an out of the box solution to handle all your custom exceptions from a single point. What you need is #ControllerAdvice annotation in your exception controller:
#ControllerAdvice
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(Exception.class)
public String exception(Exception e) {
return "error";
}
}
If you want to go deep into Springs #ExceptionHandler at individual controller level or #ControllerAdvice at global application level here is a good blog.
To handle exceptions thrown from a spring application at a single point, this is the best way to do it. #ControllerAdvice will create an aspect join-point which will intercept all the exceptions with required matching types bound to the corresponding public method.Here, public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException dataIntegrityViolationException,
WebRequest request) is handling DataIntegrityViolationException thrown out of the system at one place.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
private Logger logger = Logger.getLogger(this.getClass());
private HttpHeaders header = new HttpHeaders();
#Autowired
private MessageSource messageSource;
public GlobalControllerExceptionHandler() {
header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}
/**
* #param dataIntegrityViolationException
* #param request
* #return
*/
#ExceptionHandler({ DataIntegrityViolationException.class })
public ResponseEntity<?> handleDataIntegrityViolationException(DataIntegrityViolationException dataIntegrityViolationException,
WebRequest request) {
String message = ExceptionUtils.getMessage(dataIntegrityViolationException);
logger.error("*********BEGIN**************DataIntegrityViolationException******************BEGIN*******************\n");
logger.error(message, dataIntegrityViolationException.fillInStackTrace());
logger.error("*********ENDS**************DataIntegrityViolationException*******************ENDS*****************************\n");
return ResponseEntity.status(HttpStatus.CONFLICT).headers(header).body(dataIntegrityViolationException);
}
}

Resources