Spring RestTemplate Parse Custom Error Response - rest

Given a REST service call
http://acme.com/app/widget/123
returns:
<widget>
<id>123</id>
<name>Foo</name>
<manufacturer>Acme</manufacturer>
</widget>
This client code works:
RestTemplate restTemplate = new RestTemplate();
XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
xStreamMarshaller.getXStream().processAnnotations(
new Class[] {
Widget.class,
ErrorMessage.class
});
HttpMessageConverter<?> marshallingConverter = new MarshallingHttpMessageConverter(
xStreamMarshaller, xStreamMarshaller);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(marshallingConverter);
restTemplate.setMessageConverters(converters);
Widget w = restTemplate.getForObject(
"http://acme.com/app/widget/{id}", Widget.class, 123L);
However, calling http://acme.com/app/widget/456 returns:
<error>
<message>Widget 456 does not exist</message>
<timestamp>Wed, 12 Mar 2014 10:34:37 GMT</timestamp>
</error>
but this client code throws an Exception:
Widget w = restTemplate.getForObject(
"http://acme.com/app/widget/{id}", Widget.class, 456L);
org.springframework.web.client.HttpClientErrorException: 404 Not Found
I tried:
try {
Widget w = restTemplate.getForObject(
"http://acme.com/app/widget/{id}", Widget.class, 456L);
}
catch (HttpClientErrorException e) {
ErrorMessage errorMessage = restTemplate.getForObject(
"http://acme.com/app/widget/{id}", ErrorMessage.class, 456L);
// etc...
}
The second invocation just threw another HttpClientErrorException, plus it does not feel right calling the service twice.
Is there a way to call the service once and parse the response into a Widget on success and an ErrorMessage when not found?

Following from my comment, I checked the HttpClientErrorException JavaDoc and it does support both setting/getting the statusText as well as the responseBody. However they are optional and RestTemplate may not populate them - you'll need to try something like:
try {
Widget w = restTemplate.getForObject(
"http://acme.com/app/widget/{id}", Widget.class, 456L);
}
catch (HttpClientErrorException e) {
String responseBody = e.getResponseBodyAsString();
String statusText = e.getStatusText();
// log or process either of these...
// you'll probably have to unmarshall the XML manually (only 2 fields so easy)
}
If they are both empty/null then you may have to extend the RestTemplate class involved and populate those fields yourself and/or raise a Jira issue on the Spring site.

You can also create an object from Error response body if you like:
ObjectMapper om = new ObjectMapper();
String errorResponse = ex.getResponseBodyAsString();
MyClass obj = om.readValue(errorResponse, MyClass.class);

As you already catch the HttpClientErrorException object, it should allows you to easily extract useful information about the error and pass that to the caller.
For example:
try{
<call to rest endpoint using RestTemplate>
} catch(HttpClientErrorException e){
HttpStatus statusCode = e.getStatusCode();
String body = e.getResponseBodyAsString();
}
IMO if one needs to further de-serialize the error message body into some relevant object, that can be handled somewhere outside of the catch statement scope.

I too have found this a disturbing change in the Spring library. You used to be able to throw a ResponseStatusException and pass it the HttpStatus code and a custom message and then catch the HttpClientErrorException and simply print getMessage() to see the custom error. This no longer works. In order for me to print the custom error, I had to capture the ResponseBody as a String, getResponseBodyAsString on the HttpClientErrorException. Then I needed to parse this as a String doing some pretty hokey substring manipulation. Doing this strips out the information from the ResponseBody and gives the message sent by my server. The code to do this follows:
String message = hce.getResponseBodyAsString();
int start, end;
start = message.lastIndexOf(":", message.lastIndexOf(",")-1) + 2;
end = message.lastIndexOf(",") -1;
message = message.substring(start, end);
System.out.println(message);
When I test this using ARC or Postman, they can display the correct message after I add server.error.include-message=always to my application.properties file on the server. I am not sure what method they are using to extract the message but that would be nice to know.

Related

Feign Client Get Request HttpStatus 200 but body is null when json is very long

I have an issue with my Feign client, I get the response as well when the json not containing lot of data. But when a json is very long I get 200 status inside Response Object but body is null:
#FeignClient(name = "processSvc", url = "${xxx}")
public interface ProcessClient {
#GetMapping(value = "/v1/process/{uid}", produces = "application/json")
Response readProcess(#PathVariable("uid") String uid);
}
Any proposition for resolve this issue ?
The issue was reading a response that is larger than the entire memory allocated to the current process. So, streaming the response fixed the issue by getting the body as InputStream, then convert it to String via IOUtils.toString() :
Response response = null;
String json;
try {
response = processClient.readProcess(uid);
json = IOUtils.toString(response.body().asInputStream(), Charsets.UTF_8.name());
} catch (IOException e) {
throw new RuntimeException(e);
}

javax.ws.rs.core.Response convert to string in java1.6

I want to convert Response to string
Response response = ClientBuilder.newBuilder().sslContext(sslContext).build().target(url)
.request(MediaType.APPLICATION_JSON).get();
// I have tried as below.. It didn't work
String result = (String)response.getEntity();
Error is java.lang.ClassCastException: org.glassfish.jersey.client.HttpUrlConnector$1 cannot be cast to java.lang.String
Please not i am using Java 1.6 version and i cannot find methods like readEntity(String.class) and getEntity(String.class) from Response class.
Alternate way works as below by giving String.class in get.
But i need Response object First to check the status code then i want to convert it to String.
String response = ClientBuilder.newBuilder().sslContext(sslContext).build().target(url)
.request(MediaType.APPLICATION_JSON).get(String.class);
Please assist in solving this
The readEntity() method is the way to go:
Response response = ...
String value = response.readEntity(String.class);
I don't understand why you are saying that such method is not available for you: the ClientBuilder class (just like the readEntity() method) was also introduced in JAX-RS 2.0 (Java EE 7) as part of the Client API.
Don't you have any JAX-RS 1.x dependencies in the classpath?
If we don't use Jersey client jar, then we don't get readEntity(class) and getEntity(class). These methods are not part of javax.ws.rs.core.Response they are part of Jersey clientjar. So if we are using javax.ws.rs.core.Response, then we have to manually convert as below
javax.ws.rs.core.Response response;
String result = readInputStreamAsString((InputStream) response.getEntity());
public static String readInputStreamAsString(InputStream in) {
try {
BufferedInputStream bis = new BufferedInputStream(in);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int result = bis.read();
while (result != -1) {
byte b = (byte) result;
buf.write(b);
result = bis.read();
}
return buf.toString();
} catch (IOException ex) {
return null;
}
}

CodeFluentDuplicateException what i'm i missing

I have problem getting a "CodeFluent.Runtime.CodeFluentDuplicateException" and i'm probably missing something fundamental.
However i first followed this blog about using Servicestack and codefluents and made my own template.
I have no problems to get entities but doing a put give me an exception mentioned.
Ok maybe i have done some wrong in my template so i took another approach looking for answers i found a "complete" project using Webapi and a template, ready to use. Generate ASP .NET Web API Controllers using Templates.
This generates all the controllers and seems to work. However i have the same exeption when using the "put".
This is an example of generated controller code for Put
public HttpResponseMessage Put([FromBody]Country value)
{
if (value == null || !value.Save())
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
return Request.CreateResponse(HttpStatusCode.OK, value);
}
This is how i use the controller above inside a Xamarin.Forms solution.
public async Task UpdateAsync(Country update, bool isNewItem=false)
{
HttpClient client = new HttpClient();
// RestUrl = http://developer.xamarin.com:8081/api/todoitems{0}
var uri = new Uri(string.Format(Constants.RestUrl2, update.Id));
try
{
var json = JsonConvert.SerializeObject(update);
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
if (isNewItem)
{
response = await client.PostAsync(uri, content);
}
else
{
response = await client.PutAsync(uri, content);
}
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(#" TodoItem successfully saved.");
}
}
catch (Exception ex)
{
Debug.WriteLine(#" ERROR {0}", ex.Message);
}
}
Any suggestions of what i'm missing?
Thanks for any help
//Greg
I got the answer from the softfluent support
"..
Another case of duplicate exception is when you enable optimistic concurrency and the RowVersion of the instance you are updating is null. In this case, the stored procedure will insert the row instead of updating it.
.."
Changeing the concurrencyMode=None did the trick

Quickbooks API V2 QBD - Looking for example of how to use RecordCountQuery

I am working in .NET, using the QuickBooks V2 API to try and fetch a count of the number of Items the user's QBD.
Does anyone have a working example of how to implement the "RecordCountQuery" class to find the # of Items in QBD?
Here is one of my attempt to do so:
//
// Get a record count of the items
//
var qbItemsCount = new RecordCountQuery();
var myResults = qbItemsCount.ExecuteQuery<Item>(_oQBIDataServices.ServiceContext);
Error:
Intuit.Ipp.Exception.IdsException was unhandled by user code
HResult=-2146233088
Message= The RecordCountQuery xml must wrap an existing supported query object. Please modify your query and try again.
Source=""
ErrorCode=-2015
I gather by this that I should be using an different type in the "ExecuteQuery" method like Intuit.Ipp.Data.QBD.ItemQuery. I tried this but got a different error.
So what I am asking for is not necessarily correction to the above code, but a WORKING EXAMPLE of the RecordCountQuery class in .NET.
Thank you.
Edit - Adding .net code
RecordCountQuery query = new RecordCountQuery();
CustomerQuery custQuery = new CustomerQuery();
query.Item1 = custQuery;
List<RecordCount> customerRecordCountQuery = query.ExecuteQuery<RecordCount>(context).ToList();
string customerCount = customerRecordCountQuery[0].Count;
I hv a working java code. Please check if it is useful.
API endpoint - https://services.intuit.com/sb/recordcount/v2/{*relam_id*}
POST body
<RecordCountQuery xmlns="http://www.intuit.com/sb/cdm/v2">
<CustomerQuery/>
</RecordCountQuery>
Response
<?xml version="1.0" encoding="UTF-8"?><!--XML GENERATED by IntuitDataSyncEngine (IDS) using \\SBDomainServices\CDM\branches\3.9.0-rel-1-->
<RestResponse xmlns="http://www.intuit.com/sb/cdm/v2"
xmlns:xdb ="http://xmlns.oracle.com/xdb"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://www.intuit.com/sb/cdm/v2 ../common/RestDataFilter.xsd"><RecordCounts>
<RecordCount>
<ObjectName>Customer</ObjectName>
<Count>392</Count>
</RecordCount>
</RecordCounts></RestResponse>
Java Code
public int testGetCount() {
int objectCount = 0;
try {
QBCustomerQuery CustomerQuery = QBObjectFactory.getQBObject(
context, QBCustomerQuery.class);
QBDRecordCountService iRecordCountSer = QBDServiceFactory
.getService(context, QBDRecordCountService.class);
List<QBRecordCountImpl> recordCounts = iRecordCountSer.getCount(
context, CustomerQuery);
Iterator<QBRecordCountImpl> iterator = recordCounts.iterator();
while (iterator.hasNext()) {
QBRecordCountImpl next = iterator.next();
objectCount = Integer.parseInt(next.getCount().toString());
}
System.out.println("Customer count - " + objectCount);
} catch (QBInvalidContextException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return objectCount;
}
Thanks

How do I handle/fix "Error getting response stream (ReadDone2): ReceiveFailure" when using MonoTouch?

I am using MonoTouch to build an iPhone app. In the app I am making Web Requests to pull back information from the web services running on our server.
This is my method to build the request:
public static HttpWebRequest CreateRequest(string serviceUrl, string methodName, JsonObject methodArgs)
{
string body = "";
body = methodArgs.ToString();
HttpWebRequest request = WebRequest.Create(serviceUrl) as HttpWebRequest;
request.ContentLength = body.Length; // Set type to POST
request.Method = "POST";
request.ContentType = "text/json";
request.Headers.Add("X-JSON-RPC", methodName);
StreamWriter strm = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
strm.Write(body);
strm.Close();
return request;
}
Then I call it like this:
var request = CreateRequest(URL, METHOD_NAME, args);
request.BeginGetResponse (new AsyncCallback(ProcessResponse), request);
And ProcessResponse looks like this:
private void ProcessResponse(IAsyncResult result)
{
try
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result)) // this is where the exception gets thrown
{
using (StreamReader strm = new System.IO.StreamReader(response.GetResponseStream()))
{
JsonValue value = JsonObject.Load(strm);
// do stuff...
strm.Close();
} // using
response.Close();
} // using
Busy = false;
}
catch(Exception e)
{
Console.Error.WriteLine (e.Message);
}
}
There is another question about this issue for Monodroid and the answer there suggested explicitly closing the output stream. I tried this but it doesn't solve the problem. I am still getting a lot of ReadDone2 errors occurring.
My workaround at the moment involves just re-submitting the Web Request if an error occurs and the second attempt seems to work in most cases. These errors only happen when I am testing on the phone itself and never occur when using the Simulator.
Whenever possible try to use WebClient since it will deal automatically with a lot of details (including streams). It also makes it easier to make your request async which is often helpful for not blocking the UI.
E.g. WebClient.UploadDataAsync looks like a good replacement for the above. You will get the data, when received from the UploadDataCompleted event (sample here).
Also are you sure your request is always and only using System.Text.Encoding.ASCII ? using System.Text.Encoding.UTF8 is often usedm, by default, since it will represent more characters.
UPDATE: If you send or receive large amount to byte[] (or string) then you should look at using OpenWriteAsync method and OpenWriteCompleted event.
This is a bug in Mono, please see https://bugzilla.xamarin.com/show_bug.cgi?id=19673