spring mvc: logging exception without overriding spring's default handler - java

i want to log all server side errors. so i added
#ControllerAdvice
public class MyHandler {
#ExceptionHandler(Throwable.class)
ResponseEntity handle(HttpServletRequest req, Throwable t) {
// if t is clientError return 400
// else log and return 500
}
}
but now i have to manually list all default spring's 4xx errors like e.g. HttpMediaTypeNotSupportedException. what can i do to first let spring's default handler handles all errors it can, and only then run my MyHandler?
or maybe there is some other way of intercepting unhandled errors in spring?

Related

ExceptionHandler doesn't catch HandlerInterceptor exception if endpoint path is unknown

I have a component that implements the HandlerInterceptor interface, and implements the preHandle method. In this method I retrieve a parameter from the request, and throw an IllegalArgumentException if that parameter is missing.
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String parameter = request.getHeader("parameter123");
if (StringUtils.isEmpty(parameter)) {
throw new IllegalArgumentException("parameter123 not specified");
}
[...]
return true;
}
In another class annotated with #ControllerAdvice, I have a ExceptionHandler that catches the IllegalArgumentExceptions and turns those into a formatted response with HTTP status code 400.
When this is executed by triggering a valid path of my API, everything works just fine. Problems arise when I try to call an invalid/unexisting path of my API. The HandlerInterceptor is called and the exception is thrown but my ExceptionHandler is not triggered and the result is a basic HTTP status code 500 exception. It seems to both override the basic HTTP status 404 mechanism, while also preventing the triggering of my ExceptionHandlers (even an ExceptionHandler on Exception.class doesn't ever get called).
Any explanations regarding this behaviour are welcome ! Thanks
Although this may be an old question, I want to provide an answer for anyone who may come across it in the future.
When you raise an exception in the preHandle method of a HandlerInterceptor, it may be wrapped in another exception called NestedServletException. This is a specific exception thrown by the Spring framework.
It's worth noting that NestedServletException is a runtime exception that occurs when a servlet or filter throws an exception. It encloses the original exception and provides additional information about the location where the exception occurred.

Stacktrace of custom exception is not printed in Spring Boot 2.3

Error stacktrace is not printed in console for the custom exception that is annotated with #ResponseStatus
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalErrorException extends RuntimeException {
public InternalErrorException(String message) {
super(message);
}
public InternalErrorException(String message, Throwable throwable) {
super(message, throwable);
}
}
Throwing exception like throw new InternalErrorException("error", e), never get the stacktrace printed in the console unlesss I remove the annotation #ResponseStatus
How could I get it printed while keeping the annotation #ResponseStatus?
See Annotation Type ResponseStatus API doc.
Warning: when using this annotation on an exception class, or when setting the reason attribute of this annotation, the HttpServletResponse.sendError method will be used.
With HttpServletResponse.sendError, the response is considered complete and should not be written to any further. Furthermore, the Servlet container will typically write an HTML error page therefore making the use of a reason unsuitable for REST APIs. For such cases it is preferable to use a ResponseEntity as a return type and avoid the use of #ResponseStatus altogether.
HttpServletResponse.sendError does not throw your error and I guess it is never logged because of that.
Maybe you want to implement exception handler for that exception to get it logged.
Related question

Spring ExceptionHandler but for normal beans

I have been able to successfully use #ExceptionHandler annonated methodsorg.springframework.web.bind.annotation.ExceptionHandler in Controller Classes in my Spring projects to handle exceptions thrown by spring #RestController
Working example:
#Validated
#RestController
#RequestMapping(value = UrlsProperties.API_PATH, produces = MediaType.APPLICATION_JSON, consumes = { MediaType.APPLICATION_JSON })
#Api(value = "MyController", description = "MyController processing and forwarding controller")
public class MyController {
private static Logger log = LogManager.getLogger(MyController.class);
...
#JsonFormat
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseMessage handleMissingParams(MissingServletRequestParameterException ex) {
String name = ex.getParameterName();
log.error(name + " parameter is missing");
return new ResponseMessage(400, ex.getMessage());
}
}
I am trying to achieve the same way of exception handling but for a normal bean, [ not a controller ]. Simply adding an #ExceptionHanlder annotated method did not seem to catch the exceptions thrown by that bean's methods.
My question is how to handle exceptions thrown by a bean by writing a method inside this bean?
#ExceptionHandler annotation is not for general exception handling. It's used in controllers to convert an exception into a proper HTTP response. It won't work for normal beans, because only controllers return a response.
If any code (doesn't need to be in a bean) throws an exception and you don't handle it, it would eventually propagate up to your controller's exception handler and it would be converted to a response. That would be poor design though, as you should handle exceptions as early as you can.
What you can do is create exceptions that are meant to be propagated to your exception handlers. Your code catches an exception, then re-throws it wrapped into your own exception (such as IllegalRequestException). The handler then returns an error code and details to the caller.

Spring Exception Handling - #ControllerAdvice cannot handle HttpServletResponse#sendError()

I'm using #ControllerAdvice to implement a global exception handler but I got some issues with the use of HttpServletResponse#sendError() method.
#ExceptionHandler can catch all kinds of exception, but not HttpServletResponse#sendError() invocations. I understand that HttpServletResponse#sendError() is not an exception, but I need to process it, and then redirect to a generic error page.
I'm using Spring Security for authentication, and in the failed handler, I set status 401 to the response:
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String contentType = request.getContentType();
logger.info(contentType);
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
}
Then in the #ControllerAdvice, I tried to use #ExceptionHandler and #ResponseStatus to catch 401 but it does not work:
#ResponseStatus (value=HttpStatus.UNAUTHORIZED, reason="You don't have access right on this page")//401
#ResponseBody
#ExceptionHandler(DataIntegrityViolationException.class)
public String handleHttpStatus(DataIntegrityViolationException e){
return "genericerror";
}
Can #ExceptionHandler methods process HttpServletResponse#sendError() invocations?
Spring Security is a separate framework from Spring MVC. Spring Security is a Servlet filter (have a look in your web.xml) which intercepts requests before they reach Spring MVC. You cannot process exceptions that happen at the Spring Security level in #ControllerAdvice (as #ControllerAdvice is part of Spring MVC).
As you said it yourself, HttpServletResponse#sendError() does not throw an exception. According to this document, it sends an error response to the client using the specified status code and clears the buffer.
In your web.xml file you can define static web pages (1) for error response codes and (2) for exceptions. For example:
<error-page>
<error-code>401</error-code>
<location>/errors/genericerror</location>
</error-page>

Spring 3.2 DeferredResult - How to set status code for error response?

Spring Web 3.2 comes with a DeferredResult class for asynchronous request processing. It has a setErrorResult for providing an alternative response if something goes wrong, but no option to supply a http error code.
Surely it must be possible to control the http response code for failed requests.. How do I do that using the new Spring api?
The doc for setErrorResult method says the following:
Set an error value for the DeferredResult and handle it. The value may
be an Exception or Throwable in which case it will be processed as if
a handler raised the exception.
I suppose by setting an Exception, you may trigger an exception handler that returns the code you desire.
deferredResult.setErrorResult(new Exception());
This will always set the HTTP response code to 500. For finer control HttpServletResponse.setStatus seems to work.
This will work with user411180's client side.
public DeferredResult<List<Point>> getMessages(#RequestParam int reqestedIndex,
final HttpServletResponse response) {
final DeferredResult<List<Point>> deferredResult = new DeferredResult<>();
deferredResult.onCompletion(...);
deferredResult.onTimeout(new Runnable() {
#Override
public void run() {
deferredResult.setErrorResult("Explanation goes here.");
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); //or SC_NO_CONTENT
}
});
longPollRequests.put(deferredResult, reqestedIndex);
return deferredResult;
}
The exception that you pass as the argument to setErrorResult can be
annotated with #ResponseStatus. e.g. create an exception class of your own:
#ResponseStatus(HttpStatus.NOT_FOUND)
class NotFoundException extends RuntimeException {
// add your own constructors to set the error message
// and/or cause. See RuntimeException for valid ctors
}
Then in your code use it with the constructor you have created, for example:
deferredResult.setErrorResult(new NotFoundException(reason, cause));

Categories

Resources