#RequestBody is getting null values - spring

I have created a simple REST service (POST). But when i call this service from postman #RequestBody is not receiving any values.
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#RestController
public class Add_Policy {
#ResponseBody
#RequestMapping(value = "/Add_Policy", headers = {
"content-type=application/json" }, consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
public Policy GetIPCountry( #RequestBody Policy policy) {
System.out.println("Check value: " + policy.getPolicyNumber());
return policy;
}
}
My java Bean object is like below:
public class Policy {
private String PolicyNumber;
private String Type;
private String Tenture;
private String SDate;
private String HName;
private String Age;
public String getPolicyNumber() {
return PolicyNumber;
}
public void setPolicyNumber(String policyNumber) {
PolicyNumber = policyNumber;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public String getTenture() {
return Tenture;
}
System.out.println is printing a null as a value for PolicyNumber.
Please help me to resolve this issue.
JSON which i am passing in request body is
{
"PolicyNumber": "123",
"Type": "Test",
"Tenture": "10",
"SDate": "10-July-2016",
"HName": "Test User",
"Age": "10"
}
I have even set Content-Type to application/json in postman

Check the #RequestBody import,
wrong that will cause the problem in most cases.
import io.swagger.v3.oas.annotations.parameters.RequestBody;
to solve problem It should be
import org.springframework.web.bind.annotation.RequestBody;

Try setting the first character of the properties in your JSON to lower case. Eg.
{
"policyNumber": "123",
"type": "Test",
"tenture": "10",
"sDate": "10-July-2016",
"hName": "Test User",
"age": "10"
}
Basically, Spring uses getter and setter to set the properties of the the bean object. And it takes the property of the JSON object, matches it with the setter of the same name. eg to set the policyNumber property it tries to find a setter with the name setpolicyNumber() in your bean class and use that to set the value of your bean object.

Setter would have been missed. So, Object values do not get set.

If you are not in power to change the JSON format and still want to fix this problem, try adding
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
annotation before your DTO (Policy in example) class.

Java convention demands the name of variable in a POJO (attribute of a class) must to be the first character in lowercase.
You have uppercase letters in your JSON properties, which is what is causing the failure.

I had lombok in my pom, and lombok annotations on my bean. I did not properly installed lombok with my STS yet, and had similar issue, my bean was not populated.
When I removed lombok annotations, my bean was properly populated.
Seems like a combination of lomboc not properly installed on STS + lomboc annotations on my bean.

if you are using Lombok Api then there are no Getters and Setters publicly visible or available to the #ResponseBody and #RequestBody annotation.
That is why we read the JSON request with null values.
So you need to comment those #Getter, #Setter annotation to Receive JSON response and Read JSON request object and generate the same getters and setters.
Restart or Hot Load (using Spring-Boot-Devtools) server and it should work for sure.
You can still use your lombok api for other Entities or BO's.

In my case was a Lombok issue. I removed all the lombok annotations and created the constructor, setter and getter manually.
As an advise, I would also set the JSON to lowercase to follow the convention.

Use the annotation org.springframework.web.bind.annotation.RequestBody and not org.springframework.web.bind.annotation.ResponseBody

In my case, empty constructor must be defined.
public MyClass(){}

Apart from lowerCamelCasing, for me what additionally needed was applying #JsonProperty(value="your expected JSON KEY name") for each of the getter and setter methods and using this operator under the POJO/Bean/DTO class.
Sample Code:
#JsonProperty(value="policyNumber")
public void setPolicyNumber(String policyNumber) {
this.policyNumber = policyNumber;
}

Had the same issue but for my case only one field was not being set. A log on the request body object showed it was being recieved as null. deleted getters and setters for the field and autogenerated them using the IDE and all worked fine.
I highly suspect a mismatch in the getter and setter definition can also cause this

I have been having this issue too, but the best way i solve mine was checking on spaces after the first quotes in every initialization of fields in my json values

see spring PropertyNamingStrategy(UPPER_CAMEL_CASE,LOWER_CAMEL_CASE ,LOWER_CASE
etc... defalult SNAKE_CASE).
Spring will auto change http contorller class parameter by naming strategy, which may be not consistant with your request json
take SNAKE_CASE as a ex, when "myToken" in java controller class, you client should send my_token instead of myToken

If you are using Lombok you need compileOnly and annotationProcessor
In my case I missed the 2nd one. So I got all null values
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'

1-Make Entity class properties start with lowercase.
2-Check for Annotations.
3-Check for Constructor--> **Entity classes should have two constructor.
4-Check for Getter and Setters.

In my case, date format was given incorrectly

Related

Jackson deserialization of missing JSON number value to 0.0 in Spring

I have defined a Kotlin data class like this:
#JsonIgnoreProperties(ignoreUnknown = true)
data class MandatoryLimits(
val upper: Double,
val lower: Double
)
as part of a compound object that my Spring service receives as a request body in a POST request. When I send a test request with one value missing, e.g. like this:
"specificationLimits": {
"lower": 1.6
}
then I receive a correctly deserialised object of Type MandatoryLimits, but the missing value is set to simply 0.0.
I would have expected to get a BAD REQUEST from the service, instead. Do I need to configure something in addition?
Thanks in advance for all your help!
To fail deserialization when a Kotlin primitive is null, you need to make sure the DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES Jackson configuration flag is active.
Below is an example of ObjectMapper bean configuration that will throw an error when it tries deserializing a null Kotlin primitive.
#Configuration
class JacksonConfig {
#Bean
fun objectMapper(): ObjectMapper {
return jacksonObjectMapper()
.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true)
}
}
As a workaround I defined the data class members as nullable and added an init Block to validate the presence of both values. In case of an error, I throw the response exception manually.

Providing default values for validation in SpringBoot

I want SpringBoot to be able to provide default values for fields that the user must enter. For example, I have something like this:
*Controller class*
#PostMapping("/test")
public ResponseEntity<> myMethod(#RequestBody #Valid MyContract contract) {}
*MyContract class*
#Valid
DataObject dataObject;
*DataObject class*
#Component
public class DataObject {
private #Value("${field1.default}") String field1Default;
private String field1
public String getField1() {
return (field1 == null ? field1Default : field1);
}
}
The DataObject class needs to be created on a per request basis. There are also other places in the code where it needs to be created on demand. So I imagine it needs to be a Prototype object. But I can't figure out how to get Spring to created it properly when it creates it for the request.
Update
I have read more about #RequstBody, e.g., https://www.javadevjournal.com/spring/spring-request-response-body/ and Should spring #RequestBody class be singleton or prototype?, which explains that the object is not a Component, but a simple POJO that gets the values from the Json request. So it seems that there is no way to inject #Values from the Spring application.properties file. Is there any other way around this? Or another suggested implementation?

Spring Validation of JSON - Why do I need to add `#field`

I've finally made some progress on Spring validation (on a JSON object coming in from RabbitMQ).
However there are a couple of things I don't understand:
In the documentation, it states I can just use the annotation #NotBlank then in my method I use the annotation #Valid. However I find this wasn't doing anything. So instead I did #field:NotBlank and it worked together with the following - why did this #field do the trick?
#JsonIgnoreProperties(ignoreUnknown = true)
data class MyModel (
#field:NotBlank(message = "ID cannot be blank")
val id : String = "",
#field:NotBlank(message = "s3FilePath cannot be blank")
val s3FilePath : String = ""
)
Then the function using this model:
#Service
class Listener {
#RabbitListener(queues = ["\${newsong.queue}"])
fun received(data: MyModel) {
val factory = Validation.buildDefaultValidatorFactory()
val validator = factory.validator
val validate = validator.validate(data)
// Then this `validate` will return an array of validation errors
println(validate)
}
}
Correct me if I'm wrong however I assumed just using #Valid and this point fun received(#Valid data: MyModel) it would just throw some exception for me to catch - any idea based on my code why this could have been?
Any advice/help would be appreciated.
Thanks.
Here are the imports:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import javax.validation.*
import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.amqp.rabbit.annotation.RabbitListener
import javax.validation.constraints.NotBlank
Quoting Kotlin's documentation for annotations:
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:
class Example(#field:Ann val foo, // annotate Java field
#get:Ann val bar, // annotate Java getter
#param:Ann val quux) // annotate Java constructor parameter
So, until explicitly mention what you are annotating (field, getter or something else) in Kotlin class constructor, it won't automatically know where you want to put that annotation.

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 MVC mapping #RequestBody with variables containing a dot

I am doing a POST request to my Rest Contoller with the following object:
{
"relationship.name": "someting"
}
I'd like to map that to a POJO:
public class Request {
private String relationshipName;
// Getters, setter and contructor
}
How would I dot that?
Annotate your property with #JsonProperty
public class Request {
#JsonProperty("relationship.name")
private String relationshipName;
...
}
By default, the mapping will use the variable name as the key for the property. So without the annotation, it would expect relationshipName. #JsonProperty allows you to customize the key without changing the variable name.
Have u tried using #JsonProperty on relationshipName?
#JsonProperty(name), tells Jackson ObjectMapper to map the JSON property name to the annotated Java field's name.

Resources