Not able to call API from SpringBoot which returns Application/javaScript - spring-resttemplate

I am trying to call below API from my SpringBoot Application:
"https://api.coindesk.com/v1/bpi/currentprice.json"
Since this API is returning javaScript instead of Json is am getting error as :
no suitable HttpMessageConverter found for response type [class ] and content type [application/javascript].
kindly advise how resolve this issue. Below is my code:
package org.digital.cur.service;
public class DigitalCurService
{
public static ResponseEntity digitalCurService1()
{
final HttpHeaders headers= setHttpHeaders();
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request= new HttpEntity<>(headers);
ResponseEntity<Currentprice> responseEntity = restTemplate.exchange("https://api.coindesk.com/v1/bpi/currentprice.json", HttpMethod.GET, request, Currentprice.class );
return responseEntity;
}
private static HttpHeaders setHttpHeaders()
{
HttpHeaders headers = new HttpHeaders();
MediaType mt = new MediaType("application", "javascript");
headers.setContentType(mt);
return headers;
}
}

Related

Mock resttemplate getbody returns null pointer exception

I have a class which invokes a rest service using resttemplate.
Class MyService{
RestTemplate resttemplate = new RestTemplate();
public void handler(){
string token;
token = restTemplate.exchange(authurl, HttpMethod.POST, getHttpEntity(tenantLogin, basicAuth), String.class)
.getBody();
}
private static <T> HttpEntity<T> getHttpEntity(T jsonRequest, String authorization) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", authorization);
return new HttpEntity<T>(jsonRequest, headers);
}
}
For above my test class is as below.
Class restTemplateTest{
RestTemplate restTemplate = mock(RestTemplate.class);
Field fieldReflectionUtil =
ReflectionUtils.findField(Myservice.class, "restTemplate");
ReflectionUtils.makeAccessible(fieldReflectionUtil);
ReflectionUtils.setField(fieldReflectionUtil, handler, restTemplate);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
List<String> payload = new ArrayList<>();
when(restTemplate.exchange(Mockito.anyString(), Mockito.<HttpMethod> eq(HttpMethod.POST),
Mockito.eq(new HttpEntity<>(payload.toString(), headers)), Mockito.<Class<Object>> any()).getBody())
.thenReturn(Mockito.anyString());
}
restemplate getbody() is giving nullpointer exception.
Is there something wrong in the way resttemplate is mocked.
Thanks,
Anjana.
You are supposed to return some dummy string or json string.
String token="$11234qwer";
when(restTemplate.exchange(Mockito.anyString(), Mockito.<HttpMethod>
eq(HttpMethod.POST),
Mockito.eq(new HttpEntity<>(payload.toString(), headers)), Mockito.<Class<Object>>
any()).getBody()).thenReturn(token);

Spring Integration DSL transformer

Can I have some help here on the below issue:
Calling the transformer to transform the input object to Map Object and calling handler, the handler is missing header values added before.
Why on transforming payload to Map object losing all headers?
//Adding header here setHeader("t", "t");
#ResponseBody
public EmResponse getAuditTrail(#Valid #RequestBody NGAuditTrailEntry auditEntry) {
LOG.info("Audit Service Called, creating new audit " + auditEntry);
AuditCreationFlow.CreateAuditGateway auditGateway = applicationContext.getBean(AuditCreationFlow.CreateAuditGateway.class);
MessageBuilder messageBuilder = MessageBuilder.withPayload(auditEntry).setHeader("t", "t");
Object response = auditGateway.createAudit(messageBuilder.build());
EmResponse res = new EmResponse();
LOG.info("Done with Audit creation. Response " + response);
return res;
}
//Integration flow starts here
public IntegrationFlow createAuditGatewayFlow() {
LOG.debug("Entered to spring integration flow to create the Audit entry");
return IntegrationFlows.from("auditInputChannel")
.handle(auditObjTransformer, "transformToEjbCompatible")
.handle(ejbCaller, "callEjb")
.get();
}
//Transforming payload object to map
#Component
public class AuditObjTransformer {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
#Transformer
public Object transformToEjbCompatible(NGAuditTrailEntry ngAuditTrailEntry, Map<String, Object> headers) {
LOG.debug("Transforming the NGAuditTrailEntry To AuditEntry object which is EJB compatible");
//#TODO - Tranformation code goes here.
String s = ngAuditTrailEntry.getObjectName();
Map<String, String> m = new HashMap<>();
m.put("x", s);
return m;
}
//Here in this handler, not getting headers what I added in the rest service above.
public class EJBCaller {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
public Object callEjb(Object payload, Map<String, Object> headers) throws EJBResponseException {
LOG.debug("Calling Audit EJB to crated Audit entry.");
//#TODO EJB calling code goese here.
LOG.debug("Returned from EJB after creating Audit entry. Returned value" + payload);
return payload;
}
If the transform is other than map then no issues in headers.
Thanks,
Siva
callEjb(Object payload, Map<String, Object> headers)
If payload is a Map, you have that payload in the payload and the headers method arguments at the same time.
To make it working and carry exactly headers to that Map argument you should use #Headers annotation on it:
* Annotation which indicates that a method parameter should be bound to the headers of a
* message. The annotated parameter must be assignable to {#link java.util.Map} with
* String keys and Object values.

How to handle multiple possible response types with Retrofit2, Gson and Rx

The API i have to use sucks, and always returns HTTP 200. But sometimes there is proper response:
[{"blah": "blah"}, {"blah": "blah"}]
and sometimes, there is error:
{"error": "Something went wrong", "code": 123}
I'm using Retrofit2 with Gson converter and Rx adapter:
final Api api = new Retrofit.Builder()
.baseUrl(URL)
.client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(Api.class);
And now, when I receive error response, the onError handler is called with following exception:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80)
at rx.Subscriber.setProducer(Subscriber.java:211)
at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76)
at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
at rx.Observable.unsafeSubscribe(Observable.java:10144)
at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
How can I solve it? If I could get the response in the onError handler, I could reparse it with proper error model class. But it seems I can't get the raw response.
You can use a custom Gson deserializer to marshal both responses into a single object type. Here is a rough sketch of the idea assuming your current response type is List<Map<String, String>>, you will need to adjust based on your actual return type. I am also making the assumption that the API always returns an array on success --
public class MyResponse {
String error;
Integer code;
List<Map<String, String>> response;
}
interface MyApi {
#GET("/")
Observable<MyResponse> myCall();
}
private class MyResponseDeserializer implements JsonDeserializer<MyResponse> {
public MyResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
MyResponse response = new MyResponse();
if (json.isJsonArray()) {
// It is an array, parse the data
Type responseType = new TypeToken<List<Map<String, String>>>(){}.getType();
response.response = context.deserialize(json, responseType);
} else {
// Not an array, parse out the error info
JsonObject object = json.getAsJsonObject();
response.code = object.getAsJsonPrimitive("code").getAsInt();
response.error = object.getAsJsonPrimitive("error").getAsString();
}
return response;
}
}
Use the above to create a custom Gson
Gson gson = new GsonBuilder()
.registerTypeAdapter(MyResponse.class, new MyResponseDeserializer())
.create();
use that in your retrofit builder --
.addConverterFactory(GsonConverterFactory.create(gson))
You should also update your interface to return Observable<MyResponse>. You will get both success and error in onNext now. You'll need to inspect the object to determine if it is a successful response (response != null) or not.

Content-Type must be 'application/json-patch+json' JsonServiceClient ServiceStack

I'm trying to perform a patch with a JsonServiceClient to a service stack api as follows:
var patchRequest = new JsonPatchRequest
{
new JsonPatchElement
{
op = "replace",
path = "/firstName",
value = "Test"
}
};
_jsonClient.Patch<object>($"/testurl/{id}", patchRequest);
But I'm getting the following error:
Content-Type must be 'application/json-patch+json'
The error is clear. Is there a way to change the content type before perform the request for the JsonServiceClient?
This is the request POCO in the ServiceStack api:
[Api("Partial update .")]
[Route("/testurl/{Id}”, "PATCH")]
public class PartialTest : IReturn<PartialTestRequestResponse>, IJsonPatchDocumentRequest,
IRequiresRequestStream
{
[ApiMember(Name = “Id”, ParameterType = "path", DataType = "string", IsRequired = true)]
public string Id { get; set; }
public Stream RequestStream { get; set; }
}
public class PartialTestRequestResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
}
Service implementation:
public object Patch(PartialTest request)
{
var dbTestRecord = Repo.GetDbTestRecord(request.Id);
if (dbTestRecord == null) throw HttpError.NotFound("Record not found.");
var patch =
(JsonPatchDocument<TestRecordPoco>)
JsonConvert.DeserializeObject(Request.GetRawBody(), typeof(JsonPatchDocument<TestRecordPoco>));
if (patch == null)
throw new HttpError(HttpStatusCode.BadRequest, "Body is not a valid JSON Patch Document.");
patch.ApplyTo(dbTestRecord);
Repo.UpdateDbTestRecord(dbTestRecord);
return new PartialTestResponse();
}
I'm using Marvin.JsonPatch V 1.0.0 library.
It's still not clear where the Exception is coming from as it's not an Error within ServiceStack. If you've registered a Custom Format or Filter that throws this error please include its impl (or a link to it) as well as the full StackTrace which will identify the source of the error.
But you should never call Patch<object> as an object return type doesn't specify what Response Type to deserialize into. Since you have an IReturn<T> marker you can just send the Request DTO:
_jsonClient.Patch(new PartialTest { ... });
Which will try to deserialize the Response in the IReturn<PartialTestRequestResponse> Response DTO. But as your Request DTO implements IRequiresRequestStream it's saying you're expecting unknown bytes that doesn't conform to a normal Request DTO, in which case you likely want to use a raw HTTP Client like HTTP Utils, e.g:
var bytes = request.Url.SendBytesToUrl(
method: HttpMethods.Path,
requestBody: jsonPatchBytes,
contentType: "application/json-patch+json",
accept: MimeTypes.Json);
You could modify the ContentType of a JSON Client using a request filter, e.g:
_jsonClient.RequestFilter = req =>
req.ContentType = "application/json-patch+json";
But it's more appropriate to use a low-level HTTP Client like HTTP Utils for non-JSON Service Requests like this.

Spring integration, Setting error-handler on http outbound gateway

If I add a custom error-handler to an int-http:outbound-gateway, the response body is not unmarshalled according to the expected-response-type, instead I only get a ResponseEntity returned. My custom error handler is pretty simple:
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
private static final Logger log = LoggerFactory.getLogger(AlmaGetUserResponseErrorHandler.class);
#Override
public boolean hasError(final ClientHttpResponse response) throws IOException {
// stop http 400 from returning true to error here.
log.debug("Request has returned error code {}", response.getStatusCode());
if (response.getBody() != null) {
String returnBody = IOUtils.toString(response.getBody(), "UTF-8");
log.debug("Checking error from response, code {}, body {}", response.getStatusCode(), returnBody);
}
return false;
}
}
As soon as I remove the error-handler, it unmarshalls the XML response into my POJO correctly.
The issue above was that the MyResponseErrorHandler class was streaming out the body content before it was being passed to the marshaller for expected-response-type. Hence the body was null and a plain ResponseEntity was returned.

Resources