why Spring throws HttpMessageNotReadableException with different CAUSES for JSON - json

I have a exception handler controller where I am catching HttpMessageNotReadableException as below:
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
protected ErrorMessage handleJsonException(final HttpMessageNotReadableException ex, final HttpServletRequest request)
{
if (ex.getCause() instanceof JsonParseException)
{
// some code
}
if (ex .getCause() instanceof JsonMappingException)
{
// some code
}
}
i got different causes for POST and PUT with malformed json(the first double quotes is missing in JSON text)
{firstName":"abc","lastName":"xyz"}
POST - JsonParseException
PUT - JsonMappingException
I believe both should have the same cause "JsonParseException" as the syntax is wrong.
Can anyone suggest why spring gives different for PUT as "JsonMappingException".

Looking for something to resolve this kind a problem, I found this article -> http://www.jayway.com/2013/02/03/improve-your-spring-rest-api-part-iii/
It has a few ideas to workaround, like using "getMostSpecificCause". I'm reading it to solve my problem.

Try this
//ex -> HttpMessageNotReadableException
Throwable throwable = ex.getCause();
JsonMappingException jsonMappingException = ((JsonMappingException) throwable);
// import 'InvalidFormatException' from com.fasterxml.jackson.databind.exc package
List<JsonMappingException.Reference> references = ((InvalidFormatException)throwable).getPath();
for (JsonMappingException.Reference reference : references) {
if (reference.getFieldName() != null) {
field += reference.getFieldName() + ".";
}
}
String message = jsonMappingException.getOriginalMessage();

Related

How to properly send Mono<ResponseEntity> as JSON in Netty Reactor HTTP Server response

I am trying to figure out how to properly send response with ResponseEntity as JSON from Netty Reactor HTTP Server.
My current implementation reacts on request from WebClient and should send back response with some ResponseEntity status (let's assume just HTTP OK).
Unfortunately I'm still getting InvalidDefinitionException on the client side saying that it not possible to construct instance due to no default constructor.
I know what it means but for example Spring Webflux can have return type of rest endpoint Mono as well and no issues on client side will appear.
So is it somehow possible to properly serialize entity as JSON on server side and deserialize it at the client side?
This is my client
import org.springframework.web.reactive.function.client.WebClient;
public Mono<ResponseEntity> postRequest(final Object body, final String uri) {
return webClient.post()
.uri(uri)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(body))
.exchange()
.flatMap(clientResponse -> clientResponse.toEntity(ResponseEntity.class));
}
This is my Server
public void runWithPost(final String endpointPath, final ServerCallback callback) {
server = HttpServer.create()
.host(this.host)
.port(this.port)
.route(routes ->
routes.post(endpointPath, (request, response) ->
response.addHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.sendString(Mono.just(getJSON(callback.handleCallback())))))
.wiretap(true)
.bindNow();
System.out.println("Starting server...");
}
private String getJSON(final ResponseEntity responseEntity) {
String json = StringUtils.EMPTY;
try {
json = objectMapper.writeValueAsString(responseEntity);
System.out.println("Serialized JSON: " + json);
} catch (final JsonProcessingException ex) {
System.err.println("JSON serializer error: " + ex.getMessage());
}
return json;
}
This is callback
public interface ServerCallback {
ResponseEntity handleCallback();
}
and usage
reactiveRestServer.runWithPost("/transaction", () -> ResponseEntity.ok().build());
Unfortunately on the client side I do not get HTTP status OK but deserialization exception:
2020-04-28 16:09:35.345 ERROR 15136 --- [ctor-http-nio-2] c.a.t.t.inbound.ArpMessageServiceImpl : Type definition error: [simple type, class org.springframework.http.ResponseEntity]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 2]
2020-04-28 16:09:35.349 WARN 15136 --- [ctor-http-nio-2] io.netty.util.ReferenceCountUtil : Failed to release a message: DefaultLastHttpContent(data: PooledSlicedByteBuf(freed), decoderResult: success)
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
at io.netty.util.internal.ReferenceCountUpdater.toLiveRealRefCnt(ReferenceCountUpdater.java:74) ~[netty-common-4.1.45.Final.jar:4.1.45.Final]
What I am missing?
So I finally resolved that issue. For those who would be solving similar issue here is the answer.
The problem is that Spring Webflux converts ResponseEntity into DefaultFullHttpResponse, so that DefaultFullHttpResponse contains headers, status and also body. I resolved that issue by doing exactly the same approach.
public void runWithPost(final String endpointPath, final ServerCallback callback) {
if (server == null || server.isDisposed()) {
server = HttpServer.create()
.host(this.host)
.port(this.port)
.route(routes ->
routes.post(endpointPath, (request, response) -> processResponse(response, callback)))
.wiretap(true)
.bindNow();
logger.info("Starting server...");
} else {
logger.info("Couldn't start server because one is already running!");
}
}
and conversion is here
private NettyOutbound processResponse(final HttpServerResponse response, final ServerCallback callback) {
final ResponseEntity responseEntity = callback.handleCallback();
// set status
response.status(responseEntity.getStatusCodeValue());
// set headers
final HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
entityHeaders.entrySet().stream()
.forEach(entry -> response.addHeader(entry.getKey(), buildValue(entry.getValue())));
}
if (responseEntity.hasBody()) {
try {
final Object body = responseEntity.getBody();
if (body instanceof String) {
return response.sendString(Mono.just((String) body));
} else {
return response.send(Mono.just(Unpooled.wrappedBuffer(getBytesFromObject(body))));
}
} catch (final IOException ex) {
response.status(HttpResponseStatus.INTERNAL_SERVER_ERROR);
return response.sendString(Mono.just(ex.getMessage()));
}
}
// set body
return response.send(Mono.empty());
}
Usage is as follows:
mockReactiveRestServer.runWithPost("/transaction", () -> ResponseEntity.ok().build());

how to catch exception in spring boot rest api

i have a restcontroller with following Code
#RequestMapping(method = RequestMethod.POST, value = "/student")
public void addTopic(#RequestBody Student student) {
student.setPassword(bCryptPasswordEncoder.encode(student.getPassword()));
studentService.addStudent(student);
}
but if the json data doesn't match the Student object, or is wrong formatted an com.fasterxml.jackson.core.JsonParseException: Unexpected character ('"' (code 34)) ist thrown.
what is the best practice to prevent that
I've found that I need to catch JsonProcessingException (which JsonParseException extends from) in the #ExceptionHandler rather than JsonParseException
#ControllerAdvice
public class FeatureToggleControllerAdvice {
#ExceptionHandler(JsonProcessingException.class)
public ResponseEntity<JSONAPIDocument> handleJsonParseException(JsonProcessingException ex) {
final Error error = new Error();
error.setId(UUID.randomUUID().toString());
error.setStatus(HttpStatus.BAD_REQUEST.toString());
error.setTitle(ex.getMessage());
return new ResponseEntity<>(JSONAPIDocument
.createErrorDocument(Collections.singleton(error)), HttpStatus.NOT_FOUND);
}
}
Using JsonParseException in the above sample and nothing is caught, but using JsonProcessingException works as expected.
Use Spring ExceptionHandler to do that
You could specify an ExceptionHandler based on Exception types and also apply the error codes you want to use.
#ExceptionHandler(JsonParseException.class)
public JacksonExceptionHandler {
public ResponseEntity<String> handleError(final Exception exception) {
HttpStatus status = HttpStatus.BAD_REQUEST;
if (exception != null) {
LOGGER.warn("Responding with status code {} and exception message {}", status, exception.getMessage());
return new ResponseEntity<>(exception.getMessage(), status);
}
}
Furthermore you could make use of javax.validation to validate the entity you receive and then Spring Boot will do all the validation automagically. Just add #Valid to the body.

ResponseExceptionMapper in cxf using Client (javax.ws.rs.client.Client) API

This post does not resolve the issue: ResponseExceptionMapper in cxf client . You will notice that I did in fact register and annotate my Provider, and I tried with WebApplicationException as suggested instead of Exception/CustomException.
Problem Statement: Unable to implement custom client side exception handler using Client (javax.ws.rs.client.Client) API, and #Provider class implementing the ResponseExceptionMapper interface.
Questions:
Does Client API not support custom client side providers for exception handling?
Any literature I looked up for this problem statement uses JAXRSClientFactory implementation; I'm yet to find any using Client API for this scenario. Would I have to switch my implementation?
What is the difference between Client API and JAXRSClientFactory implementations?
I am working on a cxf Client API implementation in Java, and noticed that for http status codes above 300 cxf wraps the Response in either a WebApplicationException or ProcessingException (depending upon the response status code). The server in my case has a customized response body indicating the actual reason for an http status code !200, like below (for response code = 412):
{
"requestError": {
"serviceException": {
"messageId": "SVC4120",
"text": "Invalid Request: Invalid Coupon Code."
}
}
}
Unfortunately the WebApplicationException itself does not render this. Instead the only message captured in the exception directly is a generic "412 Precondition Failed". I can do something similar to below exception block from code snippet (includes Client API code snippet):
protected RESPOBJ invoke(String endPointUrl) throws CustomException {
Object reqPOJO = prepareRequest();
try {
if(client == null) {
ClientBuilder builder = ClientBuilder.newBuilder();
//register custom JAX-RS components
builder.register(new CustomMapper());
}
WebTarget target = client.target(endPointUrl);
//do this when queryParams exist
if(!getUriParams().isEmpty()) {
for(Map.Entry<String, String> queryParams : getUriParams().entrySet()) {
target = target.queryParam(queryParams.getKey(), queryParams.getValue());
}
}
Invocation.Builder builder = target.request();
//create headers here
MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
if(isBasicAuthRequired()) {
headers.add(AUTH_HEADER_PARAM, getBasicAuthentication());
}
headers.add(CONTENT_TYPE, getMediaType().toString());
builder.headers(headers);
builder.accept(getMediaType().toString());
//GET or POST
if(HttpMethodType.GET.equals(getHttpMethod())) {
return builder.get(RESPOBJ.class);
}
return builder.post(Entity.entity(reqPOJO, getMediaType()), RESPOBJ.class);
}
catch (Exception ex) {
if(ex instanceof ResponseProcessingException) {
ResponseProcessingException e = (ResponseProcessingException) ex;
logger.error("Unmarshalling failed: [" + e.getResponse().readEntity(String.class) + "]");
}
else if(ex instanceof WebApplicationException) {
WebApplicationException e = (WebApplicationException) ex;
logger.error("Error Response: ["+e.getResponse().readEntity(String.class) + "]");
}
throw new CustomException(ex);
}
}
However, I am looking to implement something cleaner, preferably using a custom Exception handler that implements ResponseExceptionMapper<> interface. From literature I noticed the only implementations of ResponseExceptionMapper for custom client side exception handling are using JAXRSClientFactory. My current implementation however uses the Client API (code snippet below). From a design aspect I will modify this to have a separate CustomExceptionMapper class that would be the Provider only for Exception cases, but I do not see why this Custom class is registered as a Provider (works for 200 status codes as MBR, and the MBW works always) but does not work for exception cases.
Update: While debugging and observing changes between a 200 vs >300 status code (412 in my case), I noticed that for 200 case JAXRSUtils.readFromMessageBodyReader() method gets invoked, which for the 1st time retrieves the Custom Provider. The code never gets here for status codes shown below in code snippet which should be the reason for not finding the CustomMapper. Is there any difference in how I must register my CustomExceptionMapper? Or does the Client API simply not support this functionality?
// for failure case the method above returns null (status > 300), whereas for success 200 case it executes method in last line and gets the provider.
// AbstractClient class that invokes the doReadEntity() method which in turn invokes and finds the Provider in JAXRSUtils.readFromMessageBodyReader() method code
protected <T> T readBody(Response r, Message outMessage, Class<T> cls,
Type type, Annotation[] anns) {
if (cls == Response.class) {
return cls.cast(r);
}
int status = r.getStatus();
//this is invoked for failure case
if ((status < 200 || status == 204) && r.getLength() <= 0 || status >= 300) {
return null;
}
//this for 200 status code
return ((ResponseImpl)r).doReadEntity(cls, type, anns);
}
//My custom provider code
#Provider
#Consumes
#Produces(MediaType.APPLICATION_JSON)
public class CustomMapper implements MessageBodyReader<CustomResponse>, MessageBodyWriter<CustomRequest>, ResponseExceptionMapper<CustomException> {
private Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
#Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type.isAssignableFrom(CustomResponse.class);
}
#Override
public CustomResponse readFrom(Class<CustomResponse> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
CustomResponse respObj = new CustomResponse();
//json to pojo code
return respObj;
}
#Override
public long getSize(CustomRequest reqObj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
#Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type.isAssignableFrom(CustomRequest.class);
}
#Override
public void writeTo(CustomRequest reqObj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
entityStream.write(gson.toJson(reqObj).getBytes());
}
#Override
public CustomException fromResponse(Response exceptionResponse) {
//Response obj to my CustomException code
return (CustomException);
}
}
Questions:
I'm trying to figure out what is done wrong here, and if Client API does not support custom client side exception handling for any reason?
What is the difference between Client API and JAXRSClientFactory implementations?
I also am looking into possibly using ClientResponseFilter (haven't tried this yet).
Any help appreciated. Thanks.

Multiple retrofit response and API's error handling

I'm using Retrofit and Gson to get data from REST service. It's works perfectly, but only when API not returning error. Normally, API return list of objects (as json), but when error occurring, API return a single error object.
I'm trying to obtain Call<List<Link>> but when API error occurred I'm getting Gson parse error (Expected BEGIN_OBJECT but was BEGIN_ARRAY).
I've only one solution: retrieving single string and then in enqueue's onResponse() try to parse response, but here is a lot of boilerplate code.
Is there any better solution of this problem? How to handle API's errors?
You can use next construction:
#Override
public void onResponse(Call<YourModel> call, Response<YourModel> response) {
if (response.isSuccessful()) {
// Do awesome stuff
} else if (response.code == 401) {
// Handle unauthorized
} else {
// Handle other responses
}
}
Full answer: How to get Retrofit success responce status codes
EDIT
For your case you can use Call<JsonElement> as response type and parse it in Response:
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if(response.isSuccessful()){
JsonElement jsonElement = response.body();
if(jsonElement.isJsonObject()){
JsonObject objectWhichYouNeed = jsonElement.getAsJsonObject();
}
// or you can use jsonElement.getAsJsonArray() method
//use any json deserializer to convert to your class.
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
System.out.println("Failed");
}
});

How to treat differently exceptions in JSON Methods in a Spring global exception handler?

I have a global exception handler used for every exception and I want it to treat different my JSON methods. But I want to keep it centralized.
#ControllerAdvice
public class GlobalExceptionHandler extends AbstractHandlerExceptionResolver{
#ExceptionHandler(Exception.class)
#Override
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// Omitted code like logging, message translation, etc.
String contentType = response.getContentType();
//FIXME: This do NOT WORK. contentType will be null
if(contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)){
// Add error as a header
modelAndView.setView( new MappingJackson2JsonView() );
}else{
// Add error to model
modelAndView.setViewName(MyJSPView);
}
}
After debugging I see that content type is null and I cant not use it. How could I distinguish between both calls?. For testing I coded this pair of methods:
#RequestMapping(value = "jspTest")
public String jspTest(){
throw new UserMessageException(ErrorMessages.TESTING_ERROR);
}
#RequestMapping(value = "jsonTest", produces = ContentType.JSON)
#ResponseBody
public String jsonTest(){
throw new UserMessageException(ErrorMessages.TESTING_ERROR);
}
I found a solution to the problem.
I had mistakenly mixed #ControllerAdvice and #ExceptionHandler in an AbstractHandlerExceptionResolver. They are different ways to handle an exception. See this link
So I replaced #ControllerAdvice for #Component and removed #ExceptionHandler. Now the Method handler returns in the handler parameter
I used this method:
private static boolean isJson(Object handler){
if( ! (handler instanceof HandlerMethod)){
return false;
}
RequestMapping mapping = ((HandlerMethod) handler).getMethodAnnotation(RequestMapping.class);
for(String mimeType : mapping.produces()){
if( mimeType.indexOf(MediaType.APPLICATION_JSON_VALUE) != -1 ){
return true;
}
}
// Mime types produced does not include application/json
return false;
}