Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @RequestBody without using a pojo?

I am using Spring Boot and I have a method defined like this in my controller

@PostMapping("/update)
public void update(@RequestBody User user) {
    ...
}

However in my post request from client(frontend or postman for example), I have something like this:

{
    "firstName" : "John",
    "lastName" : "Doe",
    "id" : 1234,
    "metaInfoId" : "5457sdg26sg4636"
}

The thing is my User pojo has only firstName, id and lastName instance fields in it.However in my request I have to send metaInfoId as well for some other needs. So how can I retrieve or separate metaInfoIdand User model in my controller method above?

Do I have to create another model class called UserModelRequest and add all User fields along with metaInfoId field and then use @RequestBody? Is there a way to simply remove metaInfoId from the request and then take everything else and dump it into User pojo? You could do this easily in nodejs for example(just splice your object and take what you need).

So is there a way to do it in java spring without having to create another model class?

like image 835
theprogrammer Avatar asked Dec 17 '18 20:12

theprogrammer


People also ask

Can RequestBody be optional?

requestBody consists of the content object, an optional Markdown-formatted description , and an optional required flag ( false by default).

Is @RequestBody required with @RestController?

Remember, we don't need to annotate the @RestController-annotated controllers with the @ResponseBody annotation since Spring does it by default.

Where do you need to use ResponseBody What about RequestBody?

@RequestBody : Annotation indicating a method parameter should be bound to the body of the HTTP request. @ResponseBody annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).


2 Answers

You can annotate your POJO with @JsonIgnoreProperties(ignoreUnknown = true) so the fields that it does not contain are ignored. Ex

@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
    private int id;
    private String firstName;
    private String lastName;

    ... getters & setters ...
}

Additionally, you should not include metadata in the body of your rest request. The correct place for this information would be in a headers. This would change your method signature to:

@PostMapping("/update)
public void update(@RequestBody User user, @RequestHeader(value="metaInfoId") String metaInfoId) {
    ...
}

If you must add the information in the request, then your would need to update the User pojo class to include this field (Or have it extend a class with the metaInfoId field)

Lastly, you really don't want to create a new type. You can change the signature to accept a map and retrieve the necessary info like:

@PostMapping("/update)
public void update(@RequestBody Map<String, Object> userMap) {
    String metaInfoId = userMap.get("metaInfoId");

    ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
    final User user = mapper.convertValue(userMap, User.class);
}
like image 158
mad_fox Avatar answered Sep 30 '22 15:09

mad_fox


I understand from the question that you may need two cases:

  • Send a property in the JSON that does not have a corresponding field in the User object. In that case, you need to still use the User object with the supplied fields and ignoring the rest that was provided in the JSON
  • Send additional properties in the JSON and you still need to capture them in the User object without defining those extra properties specifically in the object.

I think in both cases, it is better to use Jackson than Gson as it has richer marshaling controls over the objects. Thus, my below solution assumes that you will use Jackson with Spring web - the maven dependency for Jackson is as follows:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
</dependency>

First Case

In this case, you need Jackson to ignore the metaInfoId property when it deserializes the JSON to the User object. To achieve this, please add this annotation to the top of your User class.

@JsonIgnoreProperties(ignoreUnknown = true)
class User {
    private String firstName;
    private String lastName;

    // the rest of the class definition
}

Second Case

In the second case, you still need to capture the extra properties. I'd suggest adding a catch all HashMap that accepts the extra undefined properties and you can access it by key/value. To achieve this, you'll need to add the following to your User class, or better to create an abstract class that has this code where User extends it.

class User {
    private String firstName;
    private String lastName;
    private Map<String, Object> extra;

    @JsonAnySetter
    public void addExtra(final String key, final Object value){
        this.extra.put(key, value);  
    }

    @JsonAnyGetter
    public Map<String, Object> getExtra() {
        return Collections.unmodifiableMap(this.extra);
    }

    // the rest of the class definition
}

Hope this helps

like image 26
Eslam Nawara Avatar answered Sep 30 '22 15:09

Eslam Nawara