Could not extract response: no suitable HttpMessageConverter found for response type and content type [binary/octet-stream] - spring

So i am consuming JSON response from this URL through RestTemplate
Link:
"https://s3-ap-southeast-1.amazonaws.com/he-public-data/productdf38641.json"
My Product POJO:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ProductModel {
private String uniq_id;
private String product_name;
private int retail_price;
private int discounted_price;
private List<String> image;
private String description;
private String product_rating;
private String overall_rating;
private String brand;
}
Now when i use restTemplate to store this Array of Json object in ProductModel[].
ProductModel[] books = restTemplate.getForObject(URL, ProductModel[].class);
I am getting this error
Caused by: org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.schema.testing.domain.ProductModel] and content type [binary/octet-stream]
when i pass the same JSON object though postman to REST endpoint via POST request.
it is able to process that request.
IS this all game related to content-type.
Please help , what do i have to do next. i am not sure .
Any help is appreciated

I guess there is a solution for this exception. Try out and let me know the result.
Not any message converter can read your HTTP response, so it fails with an exception.
The main problem here is a content-type, In order to overcome this, you can introduce a custom message converter. and register it for all kinds of responses (i.e. ignore the response content-type header). Just like this
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// We are making this converter to process any kind of response, not only application/*json, which is the default behaviour
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);

Related

Converting Request scoped bean to JSON string

I was facing an issue while converting a bean with request scope to JSON using object mapper. It thows an exception like below. Not sure what am missing. I couldn't find any relatable resource on the web. Can someone explain what should be the way to do this.
Thanks in advance.
Line of code :
planInfo -> Bean with request scope
String json = new ObjectMapper().writeValueAsString(planInfo);
Exception :
"timestampSeconds":1630567890,"timestampNanos":683000000,"severity":"ERROR","thread":"qtp133294616-216","logger":"com.signup.api.service.SignupService","message":"exception \ncom.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: jdk.internal.loader.ClassLoaders$PlatformClassLoader[\"unnamedModule\"]-\u003ejava.lang.Module[\"classLoader\"]-....
PlanInfo
import lombok.Data;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
#Data
#Component
#RequestScope
public class PlanInfo {
String additionalPlanDetails;
String additionalServiceCost;
String listOfAdditionalServiceIds;
String planMinutes;
String planPrice;
String totalamt;
String setUpFee;
String selectedPlanId;
String canadaProvince;
String vatAmount;
String vatPercentage;
String netTotalAmt;
String selectedIvrPlanId;
String selectedIvrPlanMinutes;
String selectedIvrPlanPrice;
String totalPlanPrice;
String totalAmtAfterDiscount;
boolean waiveSetupFee;
String planCategory;
}

Same Generic commit object getting saved from different instances

I am using Javers version 5.1.2, with jdk 11, in my application, where I am committing Generic Object T and saving into mongodb. The Generic commit objects are actually created from generic rest service, where user can pass any Json.
Every thing is going fine on single instance. Whenever any re commit is sent with same request, Javers commit.getChanges().isEmpty() method returns true.
Issues:
1) Whenever same request to sent to different instance, commit.getChanges().isEmpty() method returns false.
2) If I commit one request, and restart the instance and then again commit, commit.getChanges().isEmpty() again returns false. Instead of true.
As a result of above issue, new version is getting created if request goes to different new instance or instance is restarted.
Could you please let me know, how we can handle this issue.
I will extract code from the project and will create a sample running project and share.
Right now, I can share few classes, please see, if these help:
//---------------------Entitiy Class:
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class ClientEntity<T> {
#Getter
#Setter
private String entityId;
#Getter
#Setter
private T commitObj;
#Getter
#Setter
private String authorName;
#Getter
#Setter
private boolean major;
#Getter
#Setter
private Map<String, String> commitProperties;
}
//--------DataIntegrator
#Service
public class DataIntegrator {
private final Javers javers;
private IVersionRepository versionDao;
private IdGenerator idGenerator;
#Inject
public DataIntegrator(Javers javers, IVersionRepository versionDao, IdGenerator idGenerator) {
this.javers = javers;
this.versionDao = versionDao;
this.idGenerator = idGenerator;
}
public <T> String commit(ClientEntity<T> clientObject) {
CommitEntity commitEntity = new CommitEntity();
commitEntity.setEntityId(clientObject.getEntityId());
commitEntity.setEntityObject(clientObject.getCommitObj());
Map<String, String> commitProperties = new HashMap<>();
commitProperties.putAll(clientObject.getCommitProperties());
commitProperties.put(commit_id_property_key, clientObject.getEntityId());
commitProperties.putAll(idGenerator.getEntityVersions(clientObject.getEntityId(), clientObject.isMajor()));
Commit commit = javers.commit(clientObject.getAuthorName(), commitEntity, commitProperties);
if (commit.getChanges().isEmpty()) {
return "No Changes Found";
}
versionDao.save(
new VersionHead(clientObject.getEntityId(), Long.parseLong(commitProperties.get(major_version_id_key)),
Long.parseLong(commitProperties.get(minor_version_id_key))));
return commit.getProperties().get(major_version_id_key) + ":"
+ commit.getProperties().get(minor_version_id_key);
}
}
1) commitObj is a Generic object, in ClientEntity, which holds Json coming from the Rest webService. The JSON can be any valid json. Can have nested structure also.
2) After calling javers.commit method, we are checking if it is existing entity or there is any change using commit.getChanges().isEmpty().
If same second request goes to same instance, it returns true for change, as expected
If same second request goes to different instance, under load balancer, it takes it as different request and commit.getChanges().isEmpty() returns false. Expected response should be true, as it is same version.
If after first request, I restart instance, and make a same request, it returns false, instead of true, which means, getChanges method taking the same request as same.

Spring request validation

I discover that Spring request validation differs from laravel-validation.
I must validate fields by type and by other constraints (e.g. max length). But Spring throws different exceptions when field cannot be casted to Dto’s field type (thrown by Jackson) or it’s just too long (thrown by jsr-303).
How can I validate JSON request by JSON-schema? I think it’s better solution.
Use javax.validation.constraints on your DTOs. Here is an example from baeldung
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Email;
public class User {
#NotNull(message = "Name cannot be null")
private String name;
#AssertTrue
private boolean working;
#Size(min = 10, max = 200, message
= "About Me must be between 10 and 200 characters")
private String aboutMe;
#Min(value = 18, message = "Age should not be less than 18")
#Max(value = 150, message = "Age should not be greater than 150")
private int age;
#Email(message = "Email should be valid")
private String email;
// standard setters and getters
}
Okay, I deal with the problem using java-json-tools/JSON-Schema-Validator library.
I have created service with only one method: “validate” which receives JsonNode and name of file with json-schema. If validation fails, validator throws my custom exception, and exception handler renders error message

Spring WebClient setting some fields to null when they are not null in the response body

I have domain class
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;
#Data
#Document
public class Bar {
#Id
private String id;
private List<String> owners;
private List<String> cFeatures;
private Integer age;
private String color;
}
I am using below code to invoke API to get data in Bar object:
import org.springframework.web.reactive.function.client.WebClient;
Mono<Bar> prop = webClient.get()
.uri("/bars/"+id)
.header("Authorization", "Bearer " + access_token)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Bar.class).log("find by id")
The problem is that I get cFeatures as null even though original JSON response
has:
"cFeatures":["custom feature one", ""]
but owners list gets correct value even though owners also has empty string value in the list (not sure if thats the source of this bug)
so Bar object has:
cFeatures: null
Is this a bug in Webclient or am I missing something ? I spent whole day on this but no fix yet.
The problem was with lombok. Lombok was generating setter method:
setCFeatures
but jackson expects setter:
setcFeatures which it does not find and hence null value for cFeatures.
It can be helpful if you make sure your POJO has the correct annotation style. For example, use jsonscheme2pojo.org and choose "Json" as your source type and "Jackson 2.x" as your annotation style. That should make the problem disappear. I was stuck at first by the same problem because I used a Gson-annotated POJO.

Post object in Angular2 to Spring RestContoller using #RequestBody

The connection between client and server works fine and the correct function addEpic is called on the server. The problem is, that just a new instance of Epic is created on the server, but the attributes from the client are not used.
#RequestBody seems to be the problem. Shouldn't convert #RequestBody automatically from the json data to the specified class?
Is the fundamental problem that Epic is a #Entity class?
It could also be that body is wrongly generated at the client.
console.log(body) shows:
{"epic":{"id":"f97d885a-410f-fa6d-7adc-291e63d35341", "name":"OurName"}}
But my swagger-ui shows for body model shema:
{"id":"string", "name":"string"}
Client
addEpic(epic:Epic):Observable<any> {
let body = JSON.stringify({epic});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.post(url + "addEpic", body, options)
.map(this.extractHeader)
.catch(this.handleError);
}
export class Epic {
id: string;
name: string;
}
Server
#RequestMapping(value="/addEpic", method = RequestMethod.POST)
public ResponseEntity<Void> addEpic(#RequestBody Epic epic) {
// Here it seems that constructor of epic is called and a new instance is created
epicRepository.saveAndFlush(epic);
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Epic implements Serializable {
private static final long serialVersionUID = -670305953055479441L;
#Column
private String id;
#Column
private String name
}
Your entity Epic has two properties 'id' and 'name'.
In JSON :
{"id":"string", "name":"string"}
This is exactly what Swagger showed you.
So your client is doing it wrong, you should create the JSON there like
let body = JSON.stringify(epic);
Just remove the superflous {} around 'epic'.

Resources