Multiple retrofit response and API's error handling - json

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");
}
});

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());

Return a string (for success or exception details) with PostJsonAsync - .NET Core REST API

I want my REST API to return a string with the exception details to the client. My client-side code is:
public async Task CreateUnit(UnitEntity unit)
{
try
{
var response = await _http.PostJsonAsync<HttpResponseMessage>("api/units", unit);
}
catch(Exception e)
{
//todo want to display error from the service and carry on
//throw;
}
}
And the code for the service / API is:
[HttpPost]
public HttpResponseMessage CreateUnit(UnitEntity unit)
{
try
{
Dal.CreateUnit(unit);
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Created;
response.Content = new StringContent($"The unit {unit.UnitName} was created successfully");
return response;
}
catch(Exception e)
{
//todo send error message in Http response if possible
//throw;
return new HttpResponseMessage() {StatusCode = HttpStatusCode.NotAcceptable, Content = new StringContent(e.Message)};
}
}
I've tried just returning a string and it basically says that the JSON serialiser can't deserialise it (because it's already a raw string and not JSON). The code above throws an exception:
Deserialization of reference types without parameterless constructor is not supported. Type 'System.Net.Http.HttpContent'

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.

How to get response in JSON format using #ExceptionHandler in Spring MVC

I am new to this #ExceptionHandler. I need to return response in JSON format if there is any exception. My code is returning response in JSON format if the operation is successful. But when any exception is thrown it is return HTML response as I have used #ExceptionHandler.
Value and reason in #ResponseStatus is coming properly but in HTML. How can I can change it to a JSON response? Please help.
In my controller class i have this methods:
#RequestMapping(value = "/savePoints", method = RequestMethod.POST, consumes = "application/json", produces = "application/json;charset=UTF-8")
public #ResponseBody
GenericResponseVO<TestResponseVO> saveScore(
#RequestBody(required = true) GenericRequestVO<TestVO> testVO) {
UserContext userCtx = new UserContext();
userCtx.setAppId("appId");
return gameHandler.handle(userCtx, testVO);
}
Exception handling method:
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Error in the process")
#ExceptionHandler(Exception.class)
public void handleAllOtherException() {
}
You can annotate the handler method with #ResponseBody and return any object you want and it should be serialized to JSON (depending on your configuration of course). For instance:
public class Error {
private String message;
// Constructors, getters, setters, other properties ...
}
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public Error handleValidationException(MethodArgumentNotValidException e) {
// Optionally do additional things with the exception, for example map
// individual field errors (from e.getBindingResult()) to the Error object
return new Error("Invalid data");
}
which should produce response with HTTP 400 code and following body:
{
"message": "Invalid data"
}
Also see Spring JavaDoc for #ExceptionHandler which lists possible return types, one of which is:
#ResponseBody annotated methods (Servlet-only) to set the response content. The return value will be converted to the response stream using message converters.
Replace
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Error in the process")
by
#ResponseStatus(value = HttpStatus.NOT_FOUND)
the 'reason' attribute force html render!
I've waste 1 day on that.....

Getting and using remote JSON data

I'm working on a little app and using GWT to build it.
I just tried making a request to a remote server which will return a response as JSON.
I've tried using the overlay types concept but I couldn't get it working. I've been changing the code around so its a bit off from where the Google GWT tutorials left.
JavaScriptObject json;
public JavaScriptObject executeQuery(String query) {
String url = "http://api.domain.com?client_id=xxxx&query=";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
URL.encode(url + query));
try {
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// violation, etc.)
}
public void onResponseReceived(Request request,
Response response) {
if (200 == response.getStatusCode()) {
// Process the response in response.getText()
json =parseJson(response.getText());
} else {
}
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
return json;
}
public static native JavaScriptObject parseJson(String jsonStr) /*-{
return eval(jsonStr );
;
}-*/;
In the chrome's debugger I get umbrellaexception, unable to see the stack trace and GWT debugger dies with NoSuchMethodError... Any ideas, pointers?
You may have a look to GWT AutoBean framework.
AutoBean allow you to serialize and deserialize JSON string from and to Plain Old Java Object.
For me this framework became essential :
Code is cleaner than with JSNI objects (JavaScript Native Interface)
No dependancy with Framework not supported by Google (like RestyGWT)
You just define interfaces with getters and setters :
// Declare any bean-like interface with matching getters and setters,
// no base type is necessary
interface Person {
Address getAddress();
String getName();
void setName(String name):
void setAddress(Address a);
}
interface Address {
String getZipcode();
void setZipcode(String zipCode);
}
Later you can serialize or deserialize JSON String using a factory (See documentation) :
// (...)
String serializeToJson(Person person) {
// Retrieve the AutoBean controller
AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person);
return AutoBeanCodex.encode(bean).getPayload();
}
Person deserializeFromJson(String json) {
AutoBean<Person> bean = AutoBeanCodex.decode(myFactory, Person.class, json);
return bean.as();
}
// (...)
First post on Stack Overflow (!) : I hope this help :)
Use JsonUtils#safeEval() to evaluate the JSON string instead of calling eval() directly.
More importantly, don't try to pass the result of an asynchronous call (like RequestBuilder#sendRequest() back to a caller using return - use a callback:
public void executeQuery(String query,
final AsyncCallback<JavaScriptObject> callback)
{
...
try {
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable caught) {
callback.onFailure(caught);
}
public void onResponseReceived(Request request, Response response) {
if (Response.SC_OK == response.getStatusCode()) {
try {
callback.onSuccess(JsonUtils.safeEval(response.getText()));
} catch (IllegalArgumentException iax) {
callback.onFailure(iax);
}
} else {
// Better to use a typed exception here to indicate the specific
// cause of the failure.
callback.onFailure(new Exception("Bad return code."));
}
}
});
} catch (RequestException e) {
callback.onFailure(e);
}
}
Generally, the workflow you're describing consists of four steps:
Make the request
Receive the JSON text
Parse the JSON in JavaScript objects
Describe these JavaScript objects using an overlay type
It sounds like you've already got steps 1 and 2 working properly.
Parse the JSON
JSONParser.parseStrict will do nicely. You'll be returned a JSONValue object.
This will allow you to avoid using your custom native method and will also make sure that it prevents arbitrary code execution while parsing the JSON. If your JSON payload is trusted and you want raw speed, use JSONParser.parseLenient. In either case, you need not write your own parser method.
Let's say that you're expecting the following JSON:
{
"name": "Bob Jones",
"occupations": [
"Igloo renovations contractor",
"Cesium clock cleaner"
]
}
Since you know that the JSON describes an object, you can tell the JSONValue that you're expecting to get a JavaScriptObject.
String jsonText = makeRequestAndGetJsonText(); // assume you've already made request
JSONValue jsonValue = JSONParser.parseStrict(jsonText);
JSONObject jsonObject = jsonValue.isObject(); // assert that this is an object
if (jsonObject == null) {
// uh oh, it wasn't an object after
// do error handling here
throw new RuntimeException("JSON payload did not describe an object");
}
Describe as an overlay type
Now that you know that your JSON describes an object, you can get that object and describe it in terms of a JavaScript class. Say you have this overlay type:
class Person {
String getName() /*-{
return this.name;
}-*/;
JsArray getOccupations() /*-{
return this.occupations;
}-*/;
}
You can make your new JavaScript object conform to this Java class by doing a cast:
Person person = jsonObject.getJavaScriptObject().cast();
String name = person.getName(); // name is "Bob Jones"
Using eval is generally dangerous, and can result in all kinds of strange behavior, if the server returns invalid JSON (note, that it's necessary, that the JSON top element is an array, if you simply use eval(jsonStr)!). So I'd make the server return a very simple result like
[ "hello" ]
and see, if the error still occurs, or if you can get a better stack trace.
Note: I assume, that the server is reachable under the same URL + port + protocol as your GWT host page (otherwise, RequestBuilder wouldn't work anyway due to Same Origin Policy.)
You actually don't need to parse the JSON, you can use native JSNI objects (JavaScript Native Interface).
Here's an example I pulled from a recent project doing basically the same thing you're doing:
public class Person extends JavaScriptObject{
// Overlay types always have protected, zero argument constructors.
protected Person(){}
// JSNI methods to get stock data
public final native String getName() /*-{ return this.name; }-*/;
public final native String getOccupation() /*-{ return this.occupation; }-*/;
// Non-JSNI methods below
}
and then to retrieve it like so:
/**
* Convert the string of JSON into JavaScript object.
*
*/
private final native JsArray<Person> asArrayOfPollData(String json) /*-{
return eval(json);
}-*/;
private void retrievePeopleList(){
errorMsgLabel.setVisible(false);
String url = JSON_URL;
url = URL.encode(url);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
try{
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request req, Response resp) {
if(resp.getStatusCode() == 200){
JsArray<Person> jsonPeople = asArrayOfPeopleData(resp.getText());
populatePeopleTable(people);
}
else{
displayError("Couldn't retrieve JSON (" + resp.getStatusText() + ")");
}
}
#Override
public void onError(Request req, Throwable arg1) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
});
} catch(RequestException e) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
}
So essentially you're casting the response as an array of JSON Objects. Good stuff.
More info here: http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html