I have a method to handle a particular class of exceptions in a Spring MVC environment.
The metod (simplified) implementation follows
#ExceptionHandler(AjaxException.class)
#ResponseStatus(value=HttpStatus.BAD_REQUEST)
#ResponseBody
public Exception handleException(AjaxException ex) {
return ex;
}
This is works fine, but to return a different ResponseStatus I have to create a new handling method.
Is it possible to change the response status inside the method body instead of using the #ResponseStatus annotation without changing the return type?
If not, is it possible to achieve the same result changing the return type (maybe serializing the exception class by myself and returning it as a string)?
Add the HttpServletResponse to the method signature and simply call the setStatus method.
#ExceptionHandler(AjaxException.class)
#ResponseBody
public Exception handleException(AjaxException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return ex;
}
Something like that should work.
Easy done, reading a little more carefully the spring documentation.
It is possible to pass the HttpServletResponse as an object parameter. In such object is possible to set the return code. The syntax is as follows:
#ExceptionHandler(AjaxException.class)
#ResponseBody
public AjaxException handleException(AjaxException ex,HttpServletResponse response) {
//test code ahead, not part of the solution
//throw new NullPointerException();
//end of test code
response.setStatus(404);//example
return ex;
}
This will return the json serialization of the exception along with the specified http return code.
EDIT:
I deleted this answer yesterday because this solution didn't seem to work. The problem was a bit trickyer: when you manage an exception this way, if the method annotated with ExceptionHandler throws an exception itself then the thrown exception is ignored and the original exception is thrown instead.
My code was somehow like the solution I posted (it threw exception in the start of the method), so I couldn't see the json output, the standard spring exception handler was fired instead. To resolve I simply trycatched the exception-throwing line and everything was ok.
Related
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.
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
In my experience, after calling Spring's RestTemplate#exchange that subsequently receives '404 - Not Found' response, a RestClientException is thrown instead of returning a ResponseEntity that can have its status checked (i.e: with ResponseEntity#getStatusCode#is4xxClientError).
I don't understand why HttpStatus#is4xxClientError even exists if instead an Exception is thrown that prevents returning a ResponseEntity with HttpStatus to call #is4xxClientError on...
I'm reasonably convinced what I just described is the actual situation, because I can find other accounts on the internet that confirm that (i.e: Spring MVC - RestTemplate launch exception when http 404 happens).
How can I get to a state in Java code using RestTemplate where I can indeed check ResponseEntity#getStatusCode#is4xxClientError when RestTemplate#exchange receives 404, rather than being prevented by the RestClientException? Is it something to do with RestTemplate configuration?
Or am I correct that is not actually possible, and maybe the existence of ResponseEntity#getStatusCode#is4xxClientError is actually a 'bug'?
RestTemplate has a method
public void setErrorHandler(ResponseErrorHandler errorHandler);
And ResponseErrorHandler interface look like this.
public interface ResponseErrorHandler {
boolean hasError(ClientHttpResponse response) throws IOException;
void handleError(ClientHttpResponse response) throws IOException;
}
Whenever RestTemplate processes a response, it first call ResponseErrorHandler#hasError(), if it returns true, ResponseErrorHandler#handleError() is called which may throw exception.
If you don't set ResponseErrorHandler#setErrorHandler(), it defaults to DefaultResponseErrorHandler.java.
Default implementation treats 4xx / 5xx series status code as error and throws RestClientException (not this exception but subclasses of this exception).
You can always write your own ResponseErrorHandler and override the default behavior. So this way you can get a ResponseEntity object even in case of 4xx / 5xx (by writing your own implementation of ResponseErrorHandler and returning always false from the method hasError()).
Once you have ResponseEntity object, you can use ResponseEntity#getStatusCode()#is4xxClientError() method for the same purpose.
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));
I have a function that is both an HTTP endpoint and a function that I call elsewhere in the Java program:
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody MyObject getObject(final HttpServletRequest request, final HttpServletResponse response)
The problem is in error handling. Functionality I would like on error:
Function called from inside Java: throws exception
Function called from HTTP: returns error json object
Trying to figure out the best way to do it. Thought about just returning a Java.lang.Object, but that's a lot of casting and type checking.
Add a Spring exception handler when calling it from web and then have the handler return your JSON.
Better approach might be to refactor the functionality into a service method then you can have the HTTP call handle the exception in it's own way and internal calls handle it however you want.
Edit
The second approach would be slightly easier to test too.