How to Configure Custom Object mapper so that Id is not a required field in the API request - objectmapper

Our API is following a JSON API specification, so it has to have an id ( from spec- "Every resource object MUST contain an id member and a type member"), but it also talks about an exception to this when the id is passed from the calling application, but also generated here when saving to DB. How can I achieve this in Springboot/Java? Can I configure a Deserialization feature in the Object mapper?
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false); ( added so id will not be mandatory , dont think this works)
return objectMapper;

Related

Object builder from http request parameters and body

Using Spring-boot there is a mapping that listens on /api/product/{productId}.
There is also a JSON payload with the structure:
{
"price": "",
"category": ""}
The current implementation creates ProductInfo object with Jackson from the JSON in the request. The productId is being read as a String with the #PathParam annotation. This works, but every method has the signature (productId, ProductInfo). Is there a declarative way to configure the deserializer, so that jackson produces an object that has productId, price and category as fields?
My understanding so far is that there is a need for a custom deserializer. Is this the correct within the Spring ecosystem?

Mask SSN in spring data rest response body

I am creating a rest service using Spring REST. In few services like Identification, I need to Mask SSN in response. (without usingJSON ignore or JsonProperty)
If clients send the SSN in the request body, the response body for SSN should be masked as shown below. (example, Postman requests body and response)
SSN should be masked as xxx-xxxx-1235 in the response body.
Is there any way in spring data rest to achieve this? Or any common solution that could be applied on all entities/controller when ResponseEntity is returned (like Interceptor)?
I believe the jackson #JsonGetter could be used for this case.
#JsonGetter("ssn")
public String getCensoredSsn() {
return Something.CensorSsn(ssn);
}
I was able to do this two ways. First was to override the get method for the required field and mask it right there. Other way was through combined use of creating CustomSerializer and using #JsonSerialize(using=CustomSerialzier) annotations at a class level.
public class CustomSerializer extends StdSerializer

Spring Data Elastic - Java.Time.Instant class Jackson deserliization not working

Following is my WorkroomDTO:
#NotNull
private Instant createdOn;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Instant changedOn;
As you can see i am using Java 8 Instant class.
In the elasticsearch Index server i store the following as JSON:
"createdOn": {
"nano": 877000000,
"epochSecond": 1579861613
},
"changedOn": {
"nano": 920000000,
"epochSecond": 1579861613
},
The problem is when i query the elasticsearch server to get me the workroom
return elasticsearchOperations.queryForPage(new NativeSearchQueryBuilder().withQuery(mainQuery)
.withPageable(elasticUtils.interceptPageable(searchDto.getPageable(), "name"))
.build(),
WorkroomDTO.class);
, i make a mapping of these fields to my WorkroomDTO i get the following exception:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"createdOn":{"nano":68000000,"epochSecond":1580127683}
FYI:
I have created a configuration file where is register explicitly the JavaTimeModule to the Object Mapper
#Configuration
public class JacksonConfiguration {
#Value("${application.serialization.include-type-key:true}")
private boolean includeTypeKey = true;
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.addHandler(new MissingTypeIdHandler());
if (includeTypeKey) {
mapper.setMixInResolver(new TypeKeyMixInResolver());
}
return mapper;
}
}
Need help!
Where does this data come from? Is it written by your application?
It seems that the used Jackson mapper does not have the jackson-datatype-jsr310 module registered.
On reading the data tries to find a constructor of Instant that can be used to create an Instant object. But Instant does not have a default constructor and the Mapper should use the Instant.ofEpochSecond(long, long) method. This page pretty declares the problem and shows how the Jackson Mapper is configured.
Storing an instant in this way, as an object with two properties, is not the right way for storing dates in Elasticsearch. You should read the Elasticsearch documentation about how Elastcisearch handles date/time fields. When storing the instant as an object like this, you loose the ability to use Elasticsearch queries with criteria based on a date/time.
Which version of Spring Data Elasticsearch do you use? Because of problems like this, from the upcoming version 4.0 on, Spring Data Elasticsearch will not use the Jackson mapper anymore for entity mapping. The MappingElasticsearchConverter supports the use of the Elasticsearch date format and the java.time classes.
Well if I'm not completely wrong, your mapping fails due to the wrong format. The json you get looks like this:
"createdOn": {
"nano": 877000000,
"epochSecond": 1579861613
},
"changedOn": {
"nano": 920000000,
"epochSecond": 1579861613
},
That means you have 2 objects 'createdOn' and 'changedOn' with two properties (nano, epochSecond), while you try to map it to one object containing two properties named 'createdOn' and 'changedOn'. You need to modify that, you have e.g. a class called Entry, with two properties (nano, epochSeconds) and then a class with two properties (createdOn, changedOn) of type Entry

#ModelAttribute vs #RequestBody, #ResponseBody

#ModelAttribute
RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method =
RequestMethod.POST)
public String processSubmit(#ModelAttribute Pet pet) { }
http://.../?name=Something&age=100
public String doSomething(#ModelAttribute User user) { }
#RequestBody
#RequestMapping(value = "/user/savecontact", method = RequestMethod.POST
public String saveContact(#RequestBody Contact contact){ }
{ "name": "Something", "age": "100" } in request body
public String doSomething(#RequestBodyUser user) { }
#ModelAttribute will take a query string. so, all the data are being pass to the server through the url
#RequestBody, all the data will be pass to the server through a full JSON body
Now which one is the best approach ???
If both are for same purpose to bind to the bean..which one is the best practice or widely used as standard practice?
Both handles multi-part file and does it both have equivalent options with one another ?
https://javabeat.net/spring-multipart-file-upload/
How do i upload/stream large images using Spring 3.2 spring-mvc in a restful way
Does any one of them has lesser capabilities then the other one? Like length limitations, method limitations. Drawbacks
Which one is more secured in terms of security ?
As the javadoc suggests, it's the usage that sets them apart, i.e., use #ModelAttribute if you want to bind the object back to the web view, if this is not needed, use #RequestBody
#RequestBody
Usecases : Restful controllers (ex: produce and consume json/xml, processing direct document download requests, searching for stuff, ajax requests )
As the name suggests the if a method argument is annotated with #RequestBody annotation Spring converts the HTTP request body to the Java type of the method argument.
Is only allowed on method parameters (#Target(value={ PARAMETER}))
The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request.
works for Post and not Get method.
#ModelAttribute
Usecases : web app controllers (ex: binding request query parameters, populating web views with options and defaults)
Uses data binders & ConversionService
Is allowed on methods and method params(#Target(value={METHOD, PARAMETER}))
Useful when dealing with model attributes for adding and retrieving model attributes to and from the Srping’s Model object
When used on METHODS, those methods are invoked before the controller methods annotated with #RequestMapping are invoked
binds a method PARAMETER or method return value to a named model attribute & the bound named model attributes are exposed to a web view
binds request query parameters to bean
For more information on Data Binding, and Type Conversion refer: https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/core.html#validation

Swagger response type getting defaulted to void unless ApiOperation.response() explicitly provided

I have an application for my REST APIs using jersey (version 1.17).
I am trying to set up swagger to document the same using swagger-maven-plugin (version 2.2)
Swagger documentation suggests:
In JAX-RS applications, the return type of the method would automatically be used,
unless it is {#code javax.ws.rs.core.Response}.In that case, the operation return type would default to void as the actual response type cannot be known.
I am using a custom object (not javax.ws.rs.core.Response) as my method return type.
But the response type remains void unless I specify it as part of the ApiOperation annotation.
#GET
#Produces({MediaType.APPLICATION_JSON})
#ApiOperation(
value = "Description",
response = MyCustomResponse.class
)
public MyCustomResponse myApiMethod() {
return apiResponse;
}
I want to remove the response value from the annotation.
What am I doing wrong here?

Resources