SpringBoot rest service error handling - java

There are multiple ways to send custom message in error response of rest api call.
Which is the best way out of these :
1.One way is to use io.swagger.annotations like
#ApiResponses(value = { #ApiResponse(code = 500, message = "error message", response=ErrorDescription.class) })
above method signature.
2.Another way is to define #ControllerAdvice over global exception hanlder.
Which is the better way of two.
Thanks

Your first approach with the swagger annotation won't handle your errors in your application. It's just for documentation purposes so that you can see how your API behaves in an error case. With this annotation, you customize your swagger-ui.html page for your REST endpoint and map the error code to a specific response object and add a custom description for your clients.
To really handle e.g. exceptions with self-defined response types and message you can use #ControllerAdvice and define the result types and messages. In addition, I would also use the Swagger annotation to write some text about the error case and tell the client which response object he can expect.
The following blog post might help you for writing your #ControllerAdvice clas: http://niels.nu/blog/2016/controller-advice-exception-handlers.html

Related

Spring Cloud AWS SQSListener Interceptor for Converter

Please take into consideration that the following example is from a SNS Topic with a SQS subscription, not a simple sqs message.
I'm currently using an SQSListener which receives a message from an SNS Topic with success:
#SqsListener("\${my-test-queue}")
fun featurePlacingListener(#NotificationMessage myJob: MyJob, #Headers headers: MessageHeaders) {
// Some code here
}
I would like to have an interceptor somehow so when there's a deserilization error I can log and send a notification.
I know this can be done If I forget using the #NotificationMessage, grab the Message part and deserialize manually but I would like to keep this clean.
Spring AOP doesn't work because the converter does the job before.
Something similar to a Controller advice or a nice AOP interceptor would be great
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Cannot construct instance of `com.example.MyJob`, problem: Parameter specified as non-null is null: method com.example.MyJob.<init>, parameter myProperty
at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertFromInternal
I just want to be able to detect when this happens.

"no result exception" for Spring #Service's

I have multiple #Service's in my Spring boot application which mainly make API calls to some external services.
Do Spring offers a custom Exception for a kind of a "404" exception? i.e. the item not found / there's no result for the requested key.
I'm aware of NoSuchElementException class but it doesn't seem to really fit here.
I guess I could create my own NoResultException but I'd like to follow the convention (if there's such)
Thanks!
There is no such exception provided by the Spring framework for the case of 404, or any other HttpStatus, for that matter.
You should ideally handle that in your service layer based on the API response and throw a custom exception which denotes the 404 case according to your domain. You can have a #ControllerAdvice which handles the specific exception and provides a custom response accordingly.
What you can use is HttpStatusCodeException. More details int the documentations. Example constructor is
HttpStatusCodeException(HttpStatus statusCode, java.lang.String statusText)
where you can provide your code, for example 404 and message. Thrown in service will be translated into the right http response.

TestRestTemplate throws exception for 4xx status codes

I am writing component tests for a Spring-Boot application, to test my security configuration. I am therefore running tests that should test for both successful responses as well as "forbidden" status.
The problem I'm encountering is that because my REST call expects a complex JSON, for blocked calls the tests fail because TestRestTemplate is trying to deserialize a response body that is not there.
I am running a Spring-Boot application, and the tests class is annotated with:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
I am trying to test a REST API that should return a list of users.
A simplified version of the call would be:
ResponseEntity<List<User>> responseEntity = testRestTemplate.exchange(URL, HttpMethod.GET, entity, new ParameterizedTypeReference<List<User>>() {});
where the TestRestTemplate is autowired by Spring, and the entity contains the authorization information.
For unauthorized request, I am getting an error like:
org.springframework.web.client.RestClientException: Error while extracting response for type [java.util.List<my.package.User>] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]
If I change the response entity to receive String instead of List, I receive the response and can check the status and see that it is "forbidden"
ResponseEntity<String> responseEntity = testRestTemplate.exchange(URL, HttpMethod.GET, null, String.class);
I know I can work around this by:
Using String and deserializing with Gson, or
Using RestTemplate instead of TestRestTemplate and handling the HttpStatusCodeException, or
Overriding methods to not try to deserialize when status code is not 2xx
but since TestRestTemplate is supposed to be a fault-tolerant convenience subclass, I would have expected it to out-of-the-box not try to deserialize on error response.
Am I missing something here? Am I using it wrong?
Apologies for resurrecting this almost 2-year-old question, but I ran into a very similar problem while working with Spring TestRestTemplate and negative validation tests.
As Martin mentioned in his answer, TestRestTemplate does not include the ResponseErrorHandler that is normally associated with the proper RestTemplate. But the body of the response will still contain an error message instead of a list of User.
In my case, my web-app had #ControllerAdvice that wrapped all the common validation errors (MethodArgumentNotValidException, MethodArgumentTypeMismatchException, etc) and returned an instance of my own class ErrorMessageDto. The controller will marshal that to JSON instead of the expected response.
My component test initially tried to catch HttpStatusCodeException because that is thrown by the normal RestTemplate. In the test, the exception was not thrown (because of the lack of ResponseErrorHandler) and my restTemplate.postForObject(path, request, MyResponse.class) simply returned an empty version of MyResponse.
After reading Martin's description and following links, I changed it to
ResponseEntity<ErrorMessageDto> responseEntity = restTemplate.postForEntity(path, request, ErrorMessageDto.class);
// Do assertions on the response code and body (using assertj)
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY);
assertThat(responseEntity.getBody().getErrors())
.extracting("path", "message")
.contains(tuple("firstName", "size must be between 0 and 255"))
In your case, I am sure the error message you are returning is an instance of an error message class. You probably realized this with your suggestion of returning a String and marshalling manually. If you know what class your error message is representing, you can simply replace that as the type in your ResponseEntity.
I expect that implementing a ResponseErrorHandler could help you work-around this.
But for RestTemplate it's the default behavior to throw errors on non-success results, are you sure you did not yet override it? Maybe you could use a dedicated RestTemplate for your test.
Source: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/HttpClientErrorException.html
Exception thrown when an HTTP 4xx is received.
For implementing a ResponseErrorHandler, see https://www.baeldung.com/spring-rest-template-error-handling
EDIT: Indeed for TestRestTemplate this is not default behavior its meant for integration tests with the following benefits:
the template behaves in a test-friendly way by not throwing exceptions on server-side errors
...
Redirects are not followed (so you can assert the response location).
Cookies are ignored (so the template is stateless).
In your case you promise in your test code that a list of users is returned while this is not true, I would not expect the code to be resilient against that, I would even say that for that case a RestTemplate might make more sense.

Spring HttpClientErrorException provides no details from response body

I'm updating legacy code that uses the exchange method of Spring 3.1 Framework's RestTemplate class. I came across what appears to be a major omission of detail. When the rest client with which I am attempting to communicate returns a 400 status code, a HttpClientErrorException is thrown but there is no response body to provide detail as to why the server rejected the request. There looks like there is no way to retrieve the response body which would provide very helpful information.
I do not need to figure out what is wrong in my calling code as I have already done that. I just would like to know if there is some way I could access and log the body of the HTTP response if an error occurs on the call.
The response body is actually a property on HttpClientErrorException. It can be accessed via the following two accessors which it inherits from its parent class HttpStatusCodeException:
public byte[] getResponseBodyAsByteArray()
public String getResponseBodyAsString()
Cast your HttpClientErrorException e to HttpStatusCodeException:
((org.springframework.web.client.HttpStatusCodeException) e).getResponseBodyAsString()

How can i set custom response phrase using Spring framework?

I am working on developing RESTful service on Java using Spring framework. If exception occurs in my services layer i throw an exception bound to Http status code. For example when user's authentication fails i send response with 401 status code.
return new ResponseEntity<>(HttpStatus.valueOf(statusCode));
I would like to explain to RESTful service consumer what is wrong with his request sending not only the code but also textual explanation. I can do this either by adding custom message to response body or by replacing default response phrase. For example replace Unauthorized for 401 with User does not exist or Incorrect password.
The problem is HttpStatus from org.springframework.http is enum and error codes are bound to textual response phrases.
Can i somehow override those response phrases or i need another approach?
And is it good practice to replace default response phrase or it would be better to put explanation message to response body?
This should work:
First you can bound the request method to the type it produces by adding
produces = {MediaType.TEXT_PLAIN} to your #RequestMapping where MediaType is from import javax.ws.rs.core.MediaType this will produce text plain response.
Then you can return the following:
return new ResponseEntity<>(message, HttpStatus.UNAUTHORIZED); where message is the text message you want to show.

Categories

Resources