I want to return HTTP status other than 200 without using the #ExceptionHandler annotation.
The reason for this is that not every call to my application, which results in a status which is not OK should throw an exception, at least not in my opinion.
As an example, if a user is trying to log into the system, but provides an inaccurate password, I see no reason to throw an exception over this just in order to be able to return a 401 status. Instead, I would like to be able to return the status from within a "regular" method.
The reason behind this is that throwing unnecessary exceptions both clutters my log files, and "uses" my log aggregator (Rollbar/Sentry) monthly allowance.
I've tried annotating methods with #ResponseStatus and #ResponseBody, but it didn't work.
I looked around for blog posts or other articles on the issue but couldn't find anything.
Any idea if this is possible?
You can use RessponseEntity. Replace the /*STATUS CODE*/ with whatever you want:
#GetMapping("/url")
#ResponseBody
public ResponseEntity<?> handlerMethod(/*parameters*/) {
// ...
return ResponseEntity.status(/*STATUS CODE*/).body(...);
}
Have a look at the following example
#RestController
public class HelloWorldController {
#GetMapping("/hello")
public ResponseEntity<String> sayHello() {
return ResponseEntity.status(HttpStatus.CREATED).body("Hello World!");
}
}
Related
Is it possible to add some custom validation message to path variable?
I have some GET
#GetMapping("/v2/tw/{id}")
public TwDto getTw(Authentication auth, #PathVariable Long id) {
In case of /v2/tw/someString I'd like to catch error and add some custom error message like "invalid tw ID"... How to do that? In ControllerAdvice add some ExceptionHandler?
For your particular use case, you can use #ExceptionHandler in the Controller or in a #ControllerAdvice class as shown here. For example, I am returning NOT_FOUND error for the sake of it.
#ExceptionHandler({MethodArgumentTypeMismatchException.class})
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "this is the reason")
public void handle() {
}
You may not see the reason in the actual error response, until you enable
server:
error:
include-message: always
If you think your #ExceptionHandler is only needed in a Controller class you can keep the method inside the controller. Alternatively you can create a #ControllerAdvice class and put the method there, so that you can reuse across multiple controllers in your application.
However, if you want a more complex validation, I will suggest to keep the id type to String and then cast manually into Long and perform the validation. Doing so you can throw your own RuntimeException and handle different cases.
I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}
I'm doing my REST documentation with swagger. I've set it up and got access on SwaggerUi and also see all my configured REST resources with their supported methods.
In my backend I have a ControllerAdvice, which does a global exception handling for all my controllers. A example exception which gets handled in the controller advice is ResourceAlreadyExistsException, when I try to create a resource which already exists, obviously. In that case my exception handler responds with a 409 CONFLICT status code.
#ExceptionHandler(value = ResourceAlreadyExistsException.class)
#ResponseStatus(HttpStatus.CONFLICT)
protected ErrorResponse handleResourceAlreadyExists(ResourceAlreadyExistsException ex, WebRequest request) {
return new ErrorResponse(ex.getMessage());
}
With this pre-condition, my create method which is mapped in the REST controller looks like this:
#RequestMapping(method = POST)
#ResponseStatus(HttpStatus.CREATED)
public RoleDto createRole(#RequestBody RoleDto roleDto) throws ResourceAlreadyExistsException {
return roleManager.createRole(roleDto);
}
With the default configuration, Swagger only shows me 201 as possible response code. Although 409 is possible too.
Of course I could add the #ApiResponse(code = 409, message = "Role already exists") definition to the createRole() method, but this seems double information as I already imply that by throwing the exception.
How can I tell swagger, that if a ResourceAlreadyExistsException can be be thrown, 409 is also a possible response code?
I've tried defining #ApiResponse on the ResourceAlreadyExistsException, but that didn't work.
That feature does not exist yet in SpringFox, although they have been looking for someone to implement it for quite some time now.
https://github.com/springfox/springfox/issues/521
This has been asked a few times in Google-land, but I can't seem to apply those resolutions to my situation. My J2EE application uses Spring and I was previously using SimpleMappingExceptionResolver to intercept Exceptions to show a friendly error page for my users. One of the guys on my team has his own package and is using an extended Exception class as a vehicle to communicate validation errors to his users. So now every time a user enters invalid data, it triggers an exception page. He refuses to change his code (even though I thing he should), so now I am forced to make error handling a package- or controller-specific thing.
I've tried to leverage the #ExceptionHandler annotation to do this, but it doesn't seem to be firing. What am I doing wrong? I am expecting a call to dmapproval to essentially return the applicationError view.
Here is my controller:
#Controller
public class ExecutiveApprovalController {
/*omitted stuff*/
#ExceptionHandler(Exception.class)
public String routToErrorHandler(Exception anExc) {
return "applicationError";
}
#RequestMapping(value = "/dmapproval", method = RequestMethod.GET)
public String dmApproval(Model model, HttpServletRequest request) throws Exception {
throw new RuntimeException(); // just for testing exception logic
}
/*omitted stuff here, too*/
}
Thanks for your help!!
Your snippet looks fine; the only difference between your code and something that I have working is that I annotate the exception handler method like this:
#ExceptionHandler(Throwable.class)
As an aside; this is far less elegant than using SimpleMappingExceptionResolver (which it sounds like you already know!). I'd ask you rogue team-mate to reconsider his validation approach and use Exceptions only in exceptional circumstances ;)
Did you add this config in your servlet-context.xml
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
I was hoping to implement a single "ExceptionController" to handle exceptions that are thrown in execution of my other controllers' methods. I hadn't specified any HandlerExceptionResolver in my application context so according to the API documentation the AnnotationMethodHandlerExceptionResolver should be started. I verified it as such in the source. So why doesn't the following work?
#Controller
public class ExceptionController {
#ExceptionHandler(NullPointerException.class)
public ModelAndView handleNullPointerException(NullPointerException ex) {
// Do some stuff
log.error(logging stuff)
return myModelAndView;
}
}
#Controller
public class AnotherController {
#RequestMapping(value="/nullpointerpath")
public String throwNullPointer() {
throw new NullPointerException();
}
}
I see in the debug logs that the three default exception handlers are asked for handling of the exception, but nothing is done and I see "DispatcherServlet - Could not complete request". Followed by the user being displayed the stacktrace and a 500 Internal error.
Make sure your Exception handler is returning a view that exists/maps to a handler.
You should write your exceptionhandler to the same class with which you want to handle, like the following.
#Controller
public class AnotherController {
#ExceptionHandler(NullPointerException.class)
public ModelAndView handleNullPointerException(NullPointerException ex) {
// Do some stuff.
log.error(logging stuff)
return myModelAndView;
}
#RequestMapping(value="/nullpointerpath")
public String throwNullPointer() {
throw new NullPointerException();
}
}
I don't think this is a good design. Controllers in Spring handle HTTP requests and map to URLs. I don't think "exception" fits into either bin. It feels like a misuse of Spring to me.
An exception is not an HTTP request. You don't map an exception to a URL. Therefore I'd conclude that controllers aren't intended to be treated as exception handlers.
Controllers are a part of the Spring API, but your design isn't using them as intended, so that's why it's not working. Re-think your design.