Spring rest json post null values - java

I have a Spring rest endpoint doing a simple hello app. It should accept a {"name":"something"} and return "Hello, something".
My controller is:
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
#RequestMapping(value="/greeting", method=RequestMethod.POST)
public String greeting(Person person) {
return String.format(template, person.getName());
}
}
Person:
public class Person {
private String name;
public Person() {
this.name = "World";
}
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
When I make a request to the service like
curl -X POST -d '{"name": "something"}' http://localhost:8081/testapp/greeting
I get
Hello, World!
Looks like it isn't deserializing the json into the Person object properly. It's using the default constructor and then not setting the name. I found this: How to create a POST request in REST to accept a JSON input? so I tried adding an #RequestBody on the controller but that causes some error about "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported". I see that is covered here: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported for #RequestBody MultiValueMap which suggests removing the #RequestBody
I have tried removing the default constructor which it doesn't like either.
This question covers null values REST webservice using Spring MVC returning null while posting JSON but it suggests adding #RequestBody but that conflicts with above...

You must set the #RequestBody to tell to Spring what should be use to set your personparam.
public Greeting greeting(#RequestBody Person person) {
return new Greeting(counter.incrementAndGet(), String.format(template, person.getName()));
}

You must set 'produces' with #RequestMapping(value="/greeting", method=RequestMethod.POST)
use below code
#RequestMapping(value="/greeting", method=RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public String greeting(#RequestBody Person person) {
return String.format(template, person.getName());
}

Related

#Valid annotation is not working for list of json request

I tried to validate the list of json request but it is not working.
Json request
[
{
"name": "AA",
"location": "Newyork"
},
{
"name": "BB",
"location": "Delhi"
}
]
Here is my controller class
Controller class
#RestController
#Validated
#RequestMapping("/v1.0")
public class Controller {
public ResponseEntity<List<Response>> ConversionResponse( #RequestBody List<#Valid Request> req) throws Throwable {
List<Response> response = new ArrayList<Response>();
for (request request : req) {
response = services.conversion(request, response);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
Here #Valid annotation doesnot validate the list
POJO class
public class Request{
#NotNull
private String name;
#NotNull(message="Location should not be null")
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
Please help me on this.
After Spring-boot 2.3.0, you need to manually add the
spring-boot-starter-validation to your project.
Use #RequestBody List<#Valid Req> reqs instead of #RequestBody #Valid List<Req> reqs.
Add #Validated on the Controller Class.
It will throw ConstraintViolationException, so you may want to map it into 400 Bad Request.
sources: Baeldung, Question 39348234

How to get Jackson to convert a JSONObject as string in the payload?

I have a POJO class which I use to serialise and deserialise between JSON messages and the POJO object for use within my Java code. In the POJO class, there is a field called requestMessage which contains a string of JSON. When the payload is sent between the services, this field is literally just a string.
For example, this is how the payload would look:
{
"name": "John Smith",
"status": true,
"requestMessage": "{\"id\": \"some-id\", \"timestamp\": \"2019-11-30\"}"
}
To cater for this field, I created an attribute requestMessage in my POJO class and made the type as JSONObject, which is a type from the org.json package. I was thinking this kind of make sense because in case I need to use it in my code, I could easily access the information as a JSONObject. I've something like this in my POJO class:
public class Message {
private String name;
private boolean status;
private JSONObject requestMessage;
#JsonCreator
public Message(
#JsonProperty("name") String name,
#JsonProperty("status") boolean status,
#JsonProperty("requestMessage") JSONObject requestMessage
) {
this.name = name;
this.status = status;
this.requestMessage = requestMessage;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public JSONObject getRequestMessage() {
return requestMessage;
}
public void setRequestMessage(JSONObject requestMessage) {
this.requestMessage = requestMessage;
}
#Override
public String toString() {
return "Message{" +
"name='" + name + '\'' +
", status=" + status +
", requestMessage=" + requestMessage +
'}';
}
}
However, it seems Jackson wasn't able to convert it as a string properly when sending out the message. The requestMessage field is always converted into a string as an empty {} object in the payload.
How can I get Jackson to convert and map the requestMessage attribute in the Message POJO class correctly as a string when it's sending out the payload?
You need to tell Jackson to serialize the JSONObject field using its toString method, like this:
public static class Message {
private String name;
private boolean status;
#JsonSerialize(using=ToStringSerializer.class)
private JSONObject requestMessage;
// ...
}
Deserialization was working because Jackson defaults to use a constructor that takes a String parameter for deserialization. JSONObject has one, so it got deserialized. I would have expected, for consistency, that toString was used on serialization, but it doesn't. I imagine there must be a good design reason behind it.
That being said, I don't understand why you try to use JSONObject from json.org if you are already using Jackson. I would stick to JSONObject's equivalent in Jackson, which I guess is JsonNode, as suggested by Coderino Javarino.

How can I detect if the JSON object within Request body is empty in Spring Boot?

I want to return an error when the body of a REST request is empty (e.g contains only {}) but there is no way to detect if the request body contains an empty JSON or not.
I tried to change #RequestBody(required = true) but it's not working.
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(#PathVariable("id") Integer id,
#Valid #RequestBody BookDto newBook) {
Book addedBook = bookService.updateBook(newBook);
return new ResponseEntity<>(addedBook,HttpStatus.OK);
}
If the body sent contains an empty JSON I should return an exception.
If the body is not empty and at least one element is provided I won't return an error.
Try #RequestBody(required = false)
This should cause the newBook parameter to be null when there is no request body.
The above still stands and is the answer to the original question.
To solve the newly edited question:
Change the #RequestBody BookDto newBook parameter to a String parameter
(for example, #RequestBody String newBookJson).
Perform pre-conversion validation (such as, "is the body an empty JSON string value").
If the body contains valid JSON,
parse the JSON into to an object (example below).
#Autowired
private ObjectMapper objectMapper; // A Jackson ObjectMapper.
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(
#PathVariable("id") Integer id,
#Valid #RequestBody String newBookJson)
{
if (isGoodStuff(newBookJson)) // You must write this method.
{
final BookDto newBook = ObjectMapper.readValue(newBookJson, BookDto.class);
... do stuff.
}
else // newBookJson is not good
{
.. do error handling stuff.
}
}
Let's suppose you have a Class BookDto :
public class BookDto {
private String bookName;
private String authorName;
}
We can use #ScriptAssert Annotation on Class BookDto:
#ScriptAssert(lang = "javascript", script = "_this.bookName != null || _this.authorName != null")
public class BookDto {
private String bookName;
private String authorName;
}
then in the resource/controller Class:
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(#PathVariable("id") Integer id,
#Valid #RequestBody BookDto newBook) {
Book addedBook = bookService.updateBook(newBook);
return new ResponseEntity<>(addedBook,HttpStatus.OK);
}
Now #Valid annotation will validate whatever we have asserted in the #ScriptAssert annotation's script attribute. i.e it now checks if the body of a REST request is empty (e.g contains only {}).

Dropwizard deserialize array

I am busy with a Dropwizard application and need to have an array injected to a POJO as one of the parameters of a put method. Unfortunately the array is not properly handled which results in a Bad Request response. To illustrate the JSON passed by the frontend looks like:
{
"name": "Jon",
"surname": "Doe",
"children": ["Chris", "Dave", "Sam"]
}
And my Java representation:
public class Person{
private String name;
private String surname;
private List<String> children;
public Person(){
}
#JsonProperty
public String getName(){
return name;
}
#JsonProperty
public void setName(String name){
this.name=name;
}
#JsonProperty
public String getSurname(){
return surname;
}
#JsonProperty
public void setsurname(String surname){
this.surname=surname;
}
#JsonProperty
public List<String> getChildren(){
return children;
}
#JsonProperty
public void setChildren(List<String> children){
this.children=children;
}
}
And in my resource class:
#PUT
#Timed
#UnitOfWork
#Path("/{userid}")
public Response getData(#PathParam("userid") LongParam userId,
Person person) {
// Do some stuff with the person
}
How can I properly handle the deserialization of the array in the JSON?
EDIT
I am using an angular front-end and I am invoking the method as follows:
function(json){
return $http({
url: API_URL.people+"/update/personID",
method: "PUT",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
data: json
});
}
Where the json argument contains the name, surname and children as above.
Looks like the GET service is defined incorrectly. It shouldn't have Person defined.
As per http method definition, the GET http method can't have body. So you can't have Person as the input parameter.
If you need to send Person to service, you may need to change the http method to POST or something else (like PUT) based on your requirement.
#GET
#Timed
#UnitOfWork
#Path("/{userid}")
public Response getData(#PathParam("userid") LongParam userId) {
// Do some stuff with the person
}
Turns out the code I provided words like a charm. Upon further investigation I made a mistake in the javascript object which got converted and sent as JSON which caused the error.

Any hint to simplify this POST? (Java)

I implemented this POST operation in Jax-RS and it is working fine, however I am sure I can send the body in other way somehow. Any hint to simplify it? (By receiving an object for instance?)
Thanks!
#POST
#Path("updateUser/{name}/{surname}/{address}")
#Produces(MediaType.TEXT_PLAIN)
public Response updateUser(#FormParam("user") String name,
#FormParam("surname") String surname,
#FormParam("address") String address) throws UnknownHostException {
User user;
user = new CorporateUser(name, surname, address);
usersService.updateUser(user);
return Response.ok(user).build();
}
You can pass json string of object by using #consumes annotaion.
#POST
#Path("/updateUser")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public Response updateUser(User bean) {
usersService.updateUser(user);
return Response.ok(user).build();
}
Add a JSON provider like Jackson to your project.
#POST
#Path("updateUser")
#Consumes(value = { MediaType.APPLICATION_JSON })
#Produces(value = { MediaType.APPLICATION_JSON })
public Response updateUser(NewCorporateUserRequest req) throws UnknownHostException {
User user;
user = new CorporateUser(req.getName(), req.getSurname(), req.getAddress());
usersService.updateUser(user);
return Response.ok().entity(user).type(MediaType.APPLICATION_JSON).build();
}
public class NewCorporateUserRequest implements java.io.Serializable {
private String name;
private String surname;
private String address;
... Getters and Setters.
}

Categories