How does field mapping happen for a PUT request body? - spring-boot

I am stuck with a basic confusion which I cannot test and cannot find a straightforward answer to as well.
I have an endpoint PUT which expects an object in the body , RequestObject.
It consists of 1 field only.
class RequestObject {
String name;
}
Now from the service from which I am hitting this endpoint, the object that I am supposed to use to send in the request has 2 fields.
class Test {
String firstname;
String age;
}
If I make a request, with age as null,
Will this work ?
Since firstName and name are not the same "spellings", will the mapping happen automatically ?
I am assuming No for both but I am not sure how to confirm.
WIll appreciate if someone can point me in right direction.
Thanks

#JsonIgnoreProperties(ignoreUnknown = true)
class RequestObject {
#JsonProperty("firstname")
String name;
}
By default Spring Boot uses the Jackson library to convert to objects. You can customize it using annotations. See https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

Related

Remove null attributes from request body Spring Boot

I'm new to spring boot and was referring to this article. It asks to add #JsonInclude(Include.NON_NULL) annotation to remove null attributes from response body, however I'm interested in removing null attributes from request body.
Please suggest how can I achieve that.
The request body is:
{
"userUuid": "2u9k2ld8f-ghj47dhj",
"suggestion": null
}
and the request DTO class is:
#Data
#NoArgsConstructor
public class UserRequestDTO implements Serializable {
private String userUuid;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String suggestion;
}
Clearly it is not working for me. When I stringify and print the request body, it includes suggestion attr which is null.
Thank you for any help.
Well... Do not use toString() use Jackson ObjectMapper. So like:
var objectMapper = new ObjectMapper();
var dto = objectMapper.readValue(JSON, UserRequestDTO.class);
var strNoNullFields = objectMapper.writeValueAsString(dto);
That way the annotation is working.
Anyway this is not something very optimal. You might want to re-think your design how to calculate checksum (your question smells a bit like XY-problem).
I need to generate the checksum of the payload excluding the null attrs. I do not want to replace null attr rather remove it. To generate the signature I will to doing .toString() on the request body
I think you are looking for a request has dynamic attributes.
Try use a HashMap<String,Object> on parameters.
Other simple way is overriding toString() from DTO and ignore null attrs.
Suggestions: Besides using this only for logging I think both is bad practice could be better create a method generateSignature() handles all rules of this.

Spring RESTful application - POST method request body mandatory attributes

I am building a RESTful app in Spring Boot and i want to make few attributes in my POST method's request body mandatory.
In swagger yaml, i mark them as required "true", but when i generate the classes using swagger editor, i dont see that impacting in any way, i.e i can't see even a #NotNull annotation or anything of that sort.
How do i mark them as mandatory in my java model class ? Is #NotNull the way to go?
If yes, should i do that in my request body class, or in the jpa document class or both ?
Thanks !
Yes, #NotNull is a way to go.
But also You need to use #Valid annotation.
check example:
#RequestMapping(value = "/appointments", method = RequestMethod.POST)
public String add(#Valid AppointmentForm form, BindingResult result) {
....
}
static class AppointmentForm {
#NotNull
private Date date;
}

Why does RestTemplate returns ArrayList<LinkedHashMap> instead of real list of model type?

It is a Springboot project. The code snip is as below. At line 59, the desired retrun type for restTemplate.getForEntity is List<Template>. While debugging, I find that the actual return type is an ArrayList contains many LinkedHashMap.
While LinkedHashMap is not sub class of Template. I don't know why the expect result type and the actual result type match.
Could anyone tell why it doesn't return ArrayList<Template>, instead of ArrayList<LinkedHashMap>? Thanks.
Template is an model defined in our project.
public class Template {
private String id;
private String name;
private String content;
xxx getters and setters
}
And it is a controller where the resttemplate is invoked.
#PostMapping(value = "/getTemplatesByGroup", produces = "application/json;charset=UTF-8")
#ResponseBody
public EUDataGrid<Template> getTemplatesByGroup(#RequestParam(defaultValue = "-1") Integer groupId) {
EUDataGrid<Template> grid = new EUDataGrid<>();
xxxx
List<Template> list = restTemplate.getForEntity(urlFullTemplates, ArrayList.class).getBody();
xxxx
return grid;
}
Json result format as below
[
{
"id": 1788,
"name": "xxxx",
"content": "xxxxx."
},
{
"id": 1787,
"name": "xxxxx",
"content": "xxxx"
}
]
Edit:
I googled a lot for this issue again. It is a common problem. There are similar scenarios some guys also encountered. I add the link in the foot of this post.
It seems that this is a bug of RestTemplate to handle generic properly. And there are ways to resolve this.
Here I want to know, why it doesn't throw exception when restTemplate returns ArrayList<LinkedHashMap> and assign it to List<Template>? They are differnt types. This is some kind of like assgin an int to a string.
I guess there is some magic with generic type. Could someone tell more about this? Thanks.
Unable to get a generic ResponseEntity<T> where T is a generic class "SomeClass<SomeGenericType>"
Using Spring RestTemplate in generic method with generic parameter
RestTemplate: how to get generic List response
Here is the problem in JSON response you are getting List<Template> or Array of Template, but in the responseType you just specified ArrayList where jackson doesn't know which type of ArrayList it is
limitation See the limitation
getForEntity(URI url, Class<T> responseType)
This sends a request to the specified URI using the GET verb and converts the response body into the requested Java type. This works great for most classes, but it has a limitation: we cannot get lists of objects.
One way is simple just specify Array type
Template[] list = restTemplate.getForEntity(urlFullTemplates, Template[].class).getBody();
Or use the exchange method

How to restrict JSON payload from containing additional fields with Spring?

I have a basic User DTO class...
public class User {
#JsonProperty("firstName")
private String firstName;
#JsonProperty("lastName")
private String lastName;
}
...and a basic request handler in a #RestController class:
#RequestMapping(path = "/users", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE })
public UserMessage createUser(#RequestBody User user){
return userService.createUser(user);
}
How can I restrict incoming JSON payloads to contain at most only the required keys?
i.e. accept this payload:
{
"firstName":"foo",
"lastName":"bar"
}
And throw a custom exception on this:
{
"firstName":"foo",
"lastName":"bar",
"type":"admin",
"asdf":"asdf"
}
I read about custom Converters, ArgumentResolvers, and I believe I could simply put an additional Map parameter in the handler and validate before service call, however I'd like to know the "best" way of handling this issue.
Regarding the User bean in your example it also already not possible, that potential other JSON fields than firstName and lastName could be mapped, simply because there are no fields in User which could hold the relevant data.
Should the User bean in your question be not complete, e.g. for simplicity reasons, and contain more fields, also then should everything be fine, as long as you did not configure your your ObjectMapper with com.fasterxml.jackson.databind.DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES => false or you use the annotation #JsonIgnoreProperties(ignoreUnknown = true) on your bean.
To sum it up: Jackson's default behavior is FAIL_ON_UNKNOWN_PROPERTIES (default: true)
For further information you can also consult the respective Deserialization docs.
Solved the issue, this thread helped
#JsonIgnoreProperties(ignoreUnknown=false) is not working in Spring 4.2.0 and upper version
mle, your answer wasn't right, since I was using the latest version of Spring Framework and the ObjectMapper's FAIL_ON_UNKNOWN_PROPERTIES is turned off by default. Additionally I was needed to set #JsonIgnoreProperties(ignoreUnknown = false) in my User DTO class (as the actual class' superclass had this set to true).
Tested it, runs like a charm, while custom errors can be handled in a #ExceptionHandler(HttpMessageNotReadableException.class) annotated handler.

Spring REST #RequestBody consume (XML or JSON) to POJO without annotations

I am writing a Springboot REST endpoint and want to consume XML or JSON requests for a simple service. In either case I want Spring to construct an #RequestBody pojo WITHOUT annotating any of the POJO. Is this OK? Safe? Performant?
I was reading this which told me about configuration by exception. To me this means if I structure my request to contain the exact name and case as the POJO member variables I want to populate the #RequestBody will be able to create my class SomeRequest.
If this is my REST endpoint:
#RequestMapping(value = GET_FOR_SOMETHING, method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE},,
produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody
StatusResponse<Boolean> getMpdForReqest(#RequestBody SomeRequest request)
And this is my POJO:
public class SomeRequest {
String one;
String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
My JSON request:
{
"one": "str",
"two": "str"
}
My XML request:
<SomeRequest>
<one>str</one>
<two>str</two>
</SomeRequest>
My question is: why should I not do this or is it perfectly fine?
Thank you all.
TLDR; It is perfectly fine.
Is this OK? Safe? Performant?
Yes, it is as performant as it's annotated cousin, if you take program efficiency into account.
If you take the Programmer efficiency into account, it is much more efficient as the developer doesn't have to deal with a bunch of annotations.
Speaking of Programmer efficiency, I would encourage you to use project Lombok instead of crapping your POJO with bunch of getter and setter methods, that's what cool kids do now a days.
Catch
This will work fine as long as your json fields are one word and small case.
When you have multi-word field name, Java standard is the camelCase and usually JSON standard is the snake_case. In this case, you can either have a Class level Annotation (one per class, so not much ugly). Or, since you are using spring boot, you can use an application wide property (spring.jackson.property-naming-strategy = SNAKE_CASE ).
If you have weird json field names with spaces in between, you might need to use #JsonProperty annotation. Remember, this is a perfectly valid json
{
"just a name with a space" : "123"
}
POJO as RequestBody works perfectly fine. Just note that Spring however will return 400 - Bad Request for every request that can not be mapped to the #RequestBody annoted object.

Resources