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
Related
I want to know what is best practice to preserve error messages when calling several micro services that is chained: I have an angular front end that calls a back end rest service which calls another rest service which calls another 3rd party service.
The 3rd party service is somewhat unreliable. And I want the response from that service to be propagated to my front end.
So to make it easier for the sake of demo’ing the problem.
I have a control class in downstream project (separate micro-service/application)
#RestController
#RequestMapping("/my-down-stream-service")
public class MyController {
#RequestMapping(value = "my-method")
public MyCustomResponse method1() {
//Some complex logic that catch exceptions and propogates a nice little message
throw new RuntimeException(“This is my exception that indicates what the response is to my 3rd party service”);
}
}
On the other micro-service calling the service above I have a restTemplate making the call to the above service
public MyResponse doIt() {
try {
restTemplate.postForEntity(“MyUrl…”, req, MyResponse.class);
} catch (final HttpStatusCodeException ex) {
//If I add a break point and inspect the exception here
}
}
I can see it is a 500 internal exception that gets send to the front end.
If I go and get the ex.getResponseBodyAsString() I get back a JSON map with the actual detail of the exception.
{
"timestamp": "2020-05-06T22:17:08.401+0200",
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"message": "This is my exception that indicates what the response is to my 3rd party service",
"path": "…"
}
And I can convert this into a map and get the message portion and construct a new exception and throw that
new ObjectMapper().readValue(ex.getResponseBodyAsString(), HashMap.class).get("message")
But this seems like a lot of work that needs to be implemented where ever I need this.
Is there a better way of doing this?
I also tried creating my own HttpStatus - Like a 550 with my "Own custom message". But you cannot set the message for the HttpStatus code dynamically aka at Runtime. Not even sure if this is the correct venture or path to go down.
My solution in the end based on Amit's suggestion
I finally ended up creating a custom class that extends springs ResponseEntityExceptionHandler. If this is on the class path of your springboot app it will intercept the exception before returning it from the controller. I also created my own exception. Reason being this way if I want my functionality to trigger I fire my own exception and everyone else can still follow the normal way. It can be changed at any time.
Also on the client side I had to cast the exception's getBody() JSON to my exception. But I didn't knew if it was my exception to start of with. So I also added some HTTP header. And on the client side I check if that header is present then I know the body is my exception and I could comfortable convert the JSON to my exception.
#ControllerAdvice
public class MyRestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {MyCustomException.class})
protected ResponseEntity<Object> handleConflict(final MyCustomException ex, final HttpServletResponse response) {
if (!response.containsHeader("MYTAG")) {
response.addHeader("EX_TYPE", "MYTAG");
}
//here you can go wild as to what type of or just the normal 500
//return ResponseEntity.status(ex.getHttpStatus()).body(ex); // 500 internal exception
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex);
}
}
If I were you, I would like to create a controller advice to handle all kind of exceptions. Then I would like to create a ErrorMessage class which will have custom errorCode, errorMessage fields as per requirements. From this controller advice, for any kind of exceptions occurred in application, it will create an instance of ErrorMessage with details like errorCode and errorMessage and wrap into ResponseEntity object (with HTTP status) and return to the other microservices.
At consumer end check the response status and act accordingly.
I think the answer you are looking for is creating an implementation of ExceptionMapper. The interface is designed to handle java exceptions that map to Response.
In your case, if the 3rd part throws an exception which is handled by the ExceptionMapper implementation you can access the error message and return that in the response.
public class ServiceExceptionMapper implements ExceptionMapper<ServiceException>
{
/**
* {#inheritDoc}
*/
#Override
public Response toResponse(ServiceException exception)
{
//grab the message from the exception and return it in the response
}
**** EDIT ****
Upon feedback, i am attaching the following disclaimer:
I KNOW that 415 is the normal code and i KNOW the normal conditions that cause it... The API i am implementing specifies this override for some reason and I cant change it because I don't control it
**** END EDIT ****
So I have the following:
#RequestMapping(method = POST, path = "/foo",
consumes = "application/json", produces = "application/json")
public ResponseEntity<Something> postFoo() {
return null;
}
When I post a message without the Content-Type header set, I get the expected 415, but I want to change that to return 406 without any boilerplate code. Is that possible? I've searched and searched, but I cant seem to find any docs telling me how to do this.
415 is the appropriate response status when the specified "Content-Type" header is not supported by the target service. 406 means "Not acceptable", which should be returned only when an invalid (or missing) "Accept" header is supplied.
The behaviour you are seeing is correct, and you shouldn't change anything.
Would you consider return new ResponseEntity(HttpStatus.NOT_ACCEPTABLE) boilerplate?
EDIT
Thanks for the clarification: the exception is thrown by Spring when processing the request, before entering your handler method. So, what you can do is change the default behavior for that particular exception. To do that, create your own ResponseEntityExceptionHandler, overriding that specific method, like this:
#ControllerAdvice
public class GlobalWebExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
/* other processing */
return new ResponseEntity(HttpStatus.NOT_ACCEPTABLE);
}
}
Here is an excellent article covering this.
I'm building an Application using Spring Boot. This application is distributed, which means I have multiple API's that call each others.
One of my underlying services interacts with a database and responds with the requested data. If a request to an unexisting ID is made, I response with a 404 HttpStatus:
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
(Same with 400 error on certain operations, or 204 for deleting an entry etc).
The problem is that I have some other Spring Boot applications that call these API's, throw an org.springframework.web.client.HttpClientErrorException: 404 Not Found Exception when they request, in this example, an unexisting entry. But the 404 status code is intended and should not return this exception (causing my Hystrix circuit breaker to call its fallback function).
How can I solve this problem?
The call to the service is implemented like this in my code: ResponseEntity<Object> data = restTemplate.getForEntity(url, Object.class);
My RestTemplate is set up like this:
private RestTemplate restTemplate = new RestTemplate();
Spring's RestTemplate uses a ResponseErrorHandler to handle errors in responses. This interface provides both a way to determine if the response has an error (ResponseErrorHandler#hasError(ClientHttpResponse)) and how to handle it (ResponseErrorHandler#handleError(ClientHttpResponse)).
You can set the RestTemplate's ResponseErrorHandler with RestTemplate#setErrorHandler(ResponseErrorHandler) whose javadoc states
By default, RestTemplate uses a DefaultResponseErrorHandler.
This default implementation
[...] checks for the status code on the
ClientHttpResponse: any code with series
HttpStatus.Series.CLIENT_ERROR or HttpStatus.Series.SERVER_ERROR is
considered to be an error. This behavior can be changed by overriding
the hasError(HttpStatus) method.
In case of an error, it throws the exception you are seeing.
If you want to change this behavior, you can provide your own ResponseErrorHandler implementation (maybe by overriding DefaultResponseErrorHandler) which doesn't consider 4xx as an error or that doesn't throw an exception.
For example
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return false; // or whatever you consider an error
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// do nothing, or something
}
});
You can then check the status code of the ResponseEntity returned by getForEntity and handle it yourself.
I want to throw an exception from the controller. How can I do that?
#RequestMapping(value = "user", method = RequestMethod.POST, headers = "Accept=application/xml, application/json")
public #ResponseBody
AppUserDTO registerUser(#RequestBody AppUserDTO userDTO) {
return userService.registerUser(userDTO);
}
In this return it will throw an exception. Is there any kind of annotation I could use? How will the exception be passed to the client side as JSON?
You appear to be using Spring-MVC. There exist a handful of exceptions which, by default, map to specific HTTP error codes. You can find a list of these here:
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-rest-spring-mvc-exceptions
Any other uncaught exception you throw will result in a HTTP 500 response.
For a detailed answer on how to use Exceptions with Spring, I suggest you read the linked page in detail or google "Spring MVC Exception".
I have this exception handler which works just fine:
#ExceptionHandler(DataNotExistException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Data not exist")
public void handleDataNotExistException() {
}
I need to put this exception handler to the exception will not be shown in the browser. Although it works, it shows the "generic" Jetty Browser error codes. I need to have a JSON-type response type.
I tried returning a Class-type error but the exception shows in the browser. I am thinking of using a Map.
If your request contains the header Accept=application/json then annotate your method with #ResponseBody and return the object that will be transformed to json. Remove the reason from #ResponseStatus.