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".
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
}
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
I defined the annotation #ExceptionHandler in my rest controller ,but it doesn't work as i wished when i mark the result type as text/plain,the method was defined to handle an attach upload request . SpringMvc just throw my business exception to the servlet container.Is it a bug of SpringMVC?How can i fix it?
#RestController
#RequestMapping("/api/test")
public class TestController extends BasicController{
#RequestMapping(value="/uploadAttach", headers = ("content-type=multipart/*"),method = RequestMethod.POST,produces="text/plain")
public String test(){
throw new ServiceException("biz exception");
}
#ExceptionHandler(value = {ServiceException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
public #ResponseBody StatusMessage serviceError(ServiceException ex) {
return new StatusMessage(ex.getMessage());
}
}
Since nobody has interest to answer this problem,i have to solve it myself.
I found that the exception handler will look for the right converter to handle the exception result which is defined as return value of the #ExceptionHandler. In my controller ,the exception is a pojo,when i debuged the program,i found there was no converter can write the pojo to type "text/plain" ,so SpringMvc throw the original exception to the servlet container,and returned an error page by Jetty.
you should follow these tips below:
1.you should define a customized exception,
but it must extend Exception.class;
2.setting a global exception hadler,and the dao throws the exception to service,the service throws to controller, then the controller uses the try...catch.. codes block to solve it;
3.a bean should be configured in SpringMVC.xml
like this:
4.and that's all, you can test it by using some simple codes like 1/0;
I have an endpoint in my controller configured as such:
#RequestMapping(value = "/users/{userId}/data", method = RequestMethod.GET)
public void getUserData(#PathVariable("userId") #Valid #NotNull Integer userId, HttpServletRequest request) {
}
If the client sends request to this endpoint with blank userId, I'm assuming Spring is interpreting the URL as /users//data because this exception is thrown:
2017-03-03 11:13:41,259 [[ACTIVE] ExecuteThread: '3' for queue:
'weblogic.kernel.Default (self-tuning)'] ERROR class
com.xxxx.web.controller.custom.ExceptionHandlingController:
RuntimeException thrown:
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type
'java.lang.Integer'; nested exception is
java.lang.NumberFormatException: For input string: "data"
What is the best way to handle this use case? I'm wary about casting userId as a String and catching the exception, that seems like a hack. I do not want to rely on the client always sending a proper request either. I am currently using Spring-Boot v1.2.6.RELEASE and am willing to upgrade versions if I know that will fix it.
You can create a class to handle global exceptions and annotate it with #ControllerAdvice.
#ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandle {
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(
MethodArgumentTypeMismatchException ex, WebRequest request) {
//Handle your exception here...
}
}
Here is a good write up on how to a lot of things you can do with #ControllerAdivce
http://www.baeldung.com/global-error-handler-in-a-spring-rest-api
You have a conflict in your request mapping.
Most likely you also have GET mapping for /users/{userId}. This is the mapping which was applied instead of the one from your question.
The issue is that you request for /users//data, web server automatically replaces double slash by single slash. The result request exactly matches to this pattern /users/{userId} but not to the one you posted. Finally spring can't cast data as an integer.
If you remove /users/{userId} (just for test reasons) you'll probably get 404 error code requesting the same url.
EDIT:
In fact you should not care that someone sends wrong requests to your REST API. REST API is a contract and both sides should follow this contract. You as a backend point should only handle a request and provide appropriate error code and good description in case if the request is wrong. data was never a correct user-id make sure this information is included into the response instead of that technical stuff.
One of the possible solutions is to validate ids using pattern.
In your case it will look like this:
#RequestMapping(value = "/users/{userId:\\d+}/data", method = GET)
#RequestMapping(value = "/users/{userId:\\d+}", method = GET)
In this case spring will automatically filter non-number ids and provide HTTP 404 for them.
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.