Unable to read application/json message in Response output - java

I'm testing REST API and while I make GET call to retrieve resources, it's resulting into 500 Internal Server Error and in output it's returning message which has media type application/json:
[
{
"messageType": "Some error type",
"messageText": "Some message text",
"moreInfo": "Some info"
}
]
Please make note that in above output, Json is inside []
I want to read value of messageText from above output response. I tried with -
JsonObject jsonObject = response.readEntity(JsonObject.class);
but it results in following error:
java.lang.IllegalStateException: Entity input stream has already been closed.
at org.glassfish.jersey.message.internal.EntityInputStream.ensureNotClosed(EntityInputStream.java:225)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:830)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
Could you please help me how can I read the message in output? I'm using Jersy library.

According to javaDoc, a call to readEntity closes the response entity, so when you make another readEntity call you get IllegalStateException.
Unless the supplied entity type is an input stream, this method
automatically closes the an unconsumed original response entity data
stream if open.
In my case, having the expression response.readEntity(String.class)
in the Expressions pane in the Debug perspective caused this exception when I ran the code in Debug mode. The evaluation of the expression consumed the entity and caused it to close.

I solved this by first doing the readEntity to a String entity and then using the Jackson ObjectMapper to actually deserialize to the target class.
Problematic code:
Transactions transObj = response.readEntity(Transactions.class);
Solution:
String stringEntity = response.readEntity(String.class);
Transactions transObj = objectMapper.readValue(stringEntity, Transactions.class);
It seems this problem arises when the JSON string in the response entity stream is very long or complex possibly requiring multiple interactions thereon. 'Deserializing' to a string seems to only require a single bite. Once you have the string (and Jackson or GSON) de-serialization to target entity takes place without touching the response.

Actually it is a issue with how are we defining the reference of Response object .
Solution is weird
Not Working one :
Response response;
if (condition) {
response =
} else {
response =
}
String resp = response.readEntity(String.class);
if (response.getStatus() == 200) {}
Working One
Response response = null;
if (condition) {
response =
} else {
response =
}
String resp = response.readEntity(String.class);
if (response.getStatus() == 200) {}
So basically if we wont assign anything to response initially , Stream will be closed

When you get your error 500 you probably get another exception in the console/log. You should start checking that and then trying to resolve this one. If you don't find anything in the log, could you please post more code?

Related

CamelHttpResponseCode is null on service error

I'm new to camel and writing a small POC to implement in an existing application. Application takes a xml request as input which contains the requested services and relevant data. It then calls those services one by one.
When a service is called successfully then I retrieve the http response code in a processor like below and do further logic:
Object code = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE);
if(null!=code && code instanceof Integer)
{
responseCode = (Integer) code;
}
In success case, responseCode received = 201
Based on the responseCode, I know if the service call is successful and then proceed with the next one.
However, I tried to produce the negative scenario by making the service url incorrect and can't see the http response code anymore:
Original service url - http://xxx:0000/.../.../.../.../...
Modified service url - http://xxx:0000/.../.../.../.../abc/...
In failure case, responseCode received = null
In postman, I get the below error:
org.apache.camel.http.common.HttpOperationFailedException: HTTP
operation failed invoking http://xxx:0000/.../.../.../.../abc/...
with statusCode: 404 at
org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:274)
at
org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:183)
I don't know why exchange doesn't contain the http response code when it's present in the error message in the postman.
I'm using onException to handle any exceptions and then calling a processor to process the flow further:
<camel:onException>
<camel:exception>java.lang.Exception</camel:exception>
<camel:process ref="xxxProcessor" />
</camel:onException>
I think I can consider responseCode=null as failure and proceed with my logic but want to understand why response code is being returned as null.
Thanks in advance!
I figured it out. It seems that in case of service exception, an instance of org.apache.camel.http.common.HttpOperationFailedException is thrown and the http status code is present in it. It can be retrieved in the processor like below:
Exception e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
if(null!=e && e instanceof HttpOperationFailedException)
{
HttpOperationFailedException httpOperationFailedException = (HttpOperationFailedException)e;
responseCode=httpOperationFailedException.getStatusCode();
}
The accepted answer helped me and it might have been valid! In the camel version I'm usin (2.20.1), getting the exception via the property does not seem to work. The following does
HttpOperationFailedException httpOperationFailedException = exchange.getException(HttpOperationFailedException.class);
if(null!=e) {
responseCode = httpOperationFailedException.getStatusCode());
}

Why is restassured MockMvcRequestSpecification returning empty response body?

In the example below I am trying to send a request with a JSON body to a mock REST API. The problem is when I try to get the response which should be JSON it returns an empty or null string. Why is this the case?
Note: when I add the .peek() to the response object it will print the expected results for me. But what I want is to capture those results in the response object. Also this one happens with the JSON string sent is bad format. I am expecting the bad results so that I can assert on them.
String jsonBody = ...
MockMvcRequestSpecification request = given()
.header("Content-Type", this.contentType)
.body(jsonBody);
ResponseOptions response = given().spec(request)
.post(this.apiUrlPath);
//This works, I can see the status code
assertThat(response.statusCode(), Matchers.equalTo(400));
//This doesn't print anything, the body is always empty
//json response is expected explaining why the request failed
//response.peek() will print what I am expecting
System.out.println(response.getBody().asString());

The requested route has not been mapped in Spark

I want to do something to sign up users with spark+java+hibernate+postgres
This is my code:
post("/registrar", (request, response) -> {
EntityManagerFactory emf = Persistence.
createEntityManagerFactory("compradorcitoPU");
EntityManager em = emf.createEntityManager();em.getTransaction().begin();
em.persist(u);
em.getTransaction().commit();
em.close(); return null; });
but this error shows up:
INFO spark.webserver.MatcherFilter - The requested route
[/registrarnull] has not been mapped in Spark
I had a similar problem. The items I'm returning are large and I wanted to write them out over stream. So, my software looked like this:
post("/apiserver", "application/json", (request, response) -> {
log.info("Received request from " + request.raw().getRemoteAddr());
ServerHandler handler = new ServerHandler();
return handler.handleRequest(request, response);
});
In my handler, I got the raw HttpResponse object, opened its OutputStream and wrote over it like so:
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.raw().getOutputStream(), records);
Since I knew I had written over the OutputStream what the caller had asked for at that point (or an error), I figured I could just return null. My program worked fine. Spark would route the request to my handler as expected. And, since I was writing over the raw OutputStream, I was getting back what was expected on the client side. But, I kept seeing the message '/apiserver route not defined' in my server logs.
In looking at the Spark documentation, it says:
The main building block of a Spark application is a set of routes. A route is made up of three simple pieces:
A verb (get, post, put, delete, head, trace, connect, options)
A path (/hello, /users/:name)
A callback (request, response) -> { }
Obviously Spark does not know what you wrote over the raw HttpResponse and as a web-server, you should be providing some response to callers. So, if your response is null, you haven't fulfilled the requirements of providing a callback and you get the error that there's no map found even if Spark behaved as expected otherwise. Just return a response (null is not a response, "200 OK" is) and the error will go away.
[Edit] Spelling and grammar.
do not "return null" instead return the empty string or something
As explained in the comments of this issue, SparkJava considers that returning null means the route has not been mapped and therefore it logs the error message and replies a response with 404 status.
To avoid such behaviour you have to return a String (possibly empty).
The error message will disappear and a response with the String as body and 200 status will be replied.
In my case, I had to implement the options request to please the preflight CORS check:
options("/*", (request,response)->{
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if(accessControlRequestMethod != null){
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});

Java processing JSON

I am getting different type of JSON response out of HTTP request API. There are might be couple of JSON format option coming back from API. For example it might be valid response with expected data but in some cases it might be internal server error detailed message.
At the moment I am using Gson to convert incoming string into the object, but since sometimes it comes as different format Gson not able to convert it as different template class is provided.
NOTE:
Error does not mean an exception. For example JSON body contain just information that authentication is failed for example, but call was made successfully and JSON body is VALID. HTTP is actually always successful and will be 200. Problem is that sometimes authentication might fail and it will return different JSON.
String response = restTemplate.getForObject(request, String.class);
ObjectResponse objResponse = gson.fromJson(response, ObjectResponse.class);
Could you please suggest better way of doing it so that I can handle different types of responses? Or maybe you know completely different way of doing it.
Thanks!
If you can't predict the structure of the response, map it to a tree of simple Java maps, arrays, and strings. The Jackson library supports this with 'readTree' methods. Once you look at the tree and decide what it is, you can then ask the library to map a tree to an object of a class.
One option is to make a class representing the JSON data, and deserialize into that. This way, if the data does not match that structure, you will get an exception.
When you try and create your object and it fails, catch the exception and try and decode it as an error - you can then deal with that case as you wish (and the potential case where it is neither the object you expect or a valid error).
Check HTTP Response Codes. If you receive a status code that isn't OK(200) then you shouldn't try to parse for a successful response. For instance you may check the code and handle response like this (the object types are not actual Java types, but are given to provide an example):
MyHttpResponse response = MyHttpHelper.execute(...);
int status = response.getMyStatusCode();
String responseData = response.getStringBody();
switch(status) {
case 200: {
//request is successful, parse valid data
break;
}
default: {
//request is not valid, parse error data
break;
}
}

Java - Check Response is JSON or not

I am creating a Server HealthCheck page. Most of the servers return JSONObject and I am able to easily parse it using:
String jsonText = readAll(br);
JSONObject json = new JSONObject(jsonText);
JSONObject resp = json.getJSONObject("Response");
Now my problem is the other servers that do not return JSON. Some are returning String, some pdf file some image and as all the responses are 200 OK I should return Positive healthcheck.
However I am using Future Threads and timing out my request after 4 secs. And all the servers which return anything other than JSON get stuck at " new JSONObject(jsonText);"
Is there any way I can check if the type of respone is JSON or not in Java?
Update:
I found my mistake - stupid one.
In the try catch block I am only catching IOException, which is not catching JSONException(and didnt show any error too - dunno y). I have added a new catch block for JSONException which works for my solution for now.
However, this isnt elegant solution, #Dave, #Radai and #Koi 's solutions is the right approach to go with.
Don't parse the string. Go back one step, and check the Content-Type header of the HTTP response. If the response contains JSON data, the Content-Type should be application/json (source).
Check the MediaType in the response using response.getMediaType(). If it is json it returns application/json.
This helped me;
if (response.getMediaType().isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
System.out.println("The media type matches application/json");
} else {
System.out.println("The media type does not match application/json");
}

Categories