I am trying to write a Micronaut service which returns a specific response when data has validation errors.
@Post(value = "/", produces = MediaType.APPLICATION_JSON)
public Response<Category> save(@Valid @Body Category category) {
Category savedCategory = categoryService.save(category);
Response<Category> response = new Response<>("Category Saved Successfully", State.SUCCESS,
savedCategory);
return response;
}
@Error(exception = ConstraintViolationException.class)
public Response<Category> onSavedFailed(HttpRequest request, ConstraintViolationException ex) {
Optional<Category> requestBody = request.getBody(Category.class);
Response<Category> response = new Response<>("", State.FAILED, requestBody.get());
response.addErrors(messageSource.violationsMessages(ex.getConstraintViolations()));
System.out.println(response); // [1] THIS LINE IS EXECUTED
return response;
}
This code seems to be working correctly because when I call the code using the following test the line marked [1] is executed properly. But the in the test after catching the HttpClientResponseException I am not able to get the response object which was returned by the controller, what am I doing wrong?
@Test
public void should_not_save_category_without_name(){
try (RxHttpClient client = embeddedServer.getApplicationContext()
.createBean(RxHttpClient.class, embeddedServer.getURL())) {
Category category = new Category("");
Response<Category> retrieve = client.toBlocking()
.retrieve(HttpRequest.POST("/categories", category),
Argument.of(Response.class, Category.class));
}catch (HttpClientResponseException exception){
System.out.println(exception.getResponse().getStatus());
System.out.println(exception);
}
}
This is what is printed in the test case
INTERNAL_SERVER_ERROR
io.micronaut.http.client.exceptions.HttpClientResponseException: Internal Server Error
Here are the micronaut client logs
10:11:51.571 [nioEventLoopGroup-1-2] DEBUG i.m.http.client.DefaultHttpClient - Sending HTTP Request: POST /categories
10:11:51.572 [nioEventLoopGroup-1-2] DEBUG i.m.http.client.DefaultHttpClient - Chosen Server: localhost(6140)
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - host: localhost:6140
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - connection: close
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - content-type: application/json
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - content-length: 2
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - Request Body
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - ----
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - {}
10:11:51.574 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - ----
Response{message='', errors=[name must not be blank], state=FAILED, data=Category{id=null, name=null}}
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - HTTP Client Response Received for Request: POST http://localhost:6140/categories
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - Status Code: 500 Internal Server Error
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - Date: Wed, 8 Jan 2020 04:41:51 GMT
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - content-type: application/json
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - content-length: 64
10:11:51.831 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - connection: close
10:11:51.832 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - Response Body
10:11:51.832 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - ----
10:11:51.832 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - {"errors":["name must not be blank"],"state":"FAILED","data":{}}
10:11:51.832 [nioEventLoopGroup-1-2] TRACE i.m.http.client.DefaultHttpClient - ----
To get the error body you need to use the 3 parameters method in which the 3rd one is the error type: https://docs.micronaut.io/latest/api/io/micronaut/http/client/DefaultHttpClient.html#exchange-io.micronaut.http.HttpRequest-io.micronaut.core.type.Argument-io.micronaut.core.type.Argument-
So, your code should be something like:
HttpRequest request = HttpRequest.POST("/categories", category);
client.toBlocking().exchange(request, Argument.of(Object.class), Argument.of(JsonError));
...
exception.getResponse().getBody(Argument.of(Response.class, Category.class)) // This return an Optional
Also, to be make sure that everything works as expected and you're actually getting the error you can enable the trace for the HttpClient
adding the following to logback.xml
:
<configuration>
...
...
<logger name="io.micronaut.http.client" level="TRACE" />
</configuration>