Spring Rest Controller Return Specific Fields

greyfox picture greyfox · May 31, 2015 · Viewed 19.2k times · Source

I've been going through my head the best way to design a JSON API using Spring MVC. As we all know IO is expensive, and thus I don't want to make the client make several API calls to get what they need. However at the same time I don't necessarily want to return the kitchen sink.

As an example I was working on a game API similar to IMDB but for video games instead.

If I returned everything connected to Game it would look something like this.

/api/game/1

{
    "id": 1,
    "title": "Call of Duty Advanced Warfare",
    "release_date": "2014-11-24",
    "publishers": [
        {
            "id": 1,
            "name": "Activision"
        }
    ],
    "developers": [
        {
            "id": 1,
            "name": "Sledge Hammer"
        }
    ],
    "platforms": [
        {
            "id": 1,
            "name": "Xbox One",
            "manufactorer": "Microsoft",
            "release_date": "2013-11-11"
        },
        {
            "id": 2,
            "name": "Playstation 4",
            "manufactorer": "Sony",
            "release_date": "2013-11-18"
        },
        {
            "id": 3,
            "name": "Xbox 360",
            "manufactorer": "Microsoft",
            "release_date": "2005-11-12"
        }
    ],
    "esrbRating": {
        "id": 1,
        "code": "T",
        "name": "Teen",
        "description": "Content is generally suitable for ages 13 and up. May contain violence, suggestive themes, crude humor, minimal blood, simulated gambling and/or infrequent use of strong language."
    },
    "reviews": [
        {
            "id": 1,
            "user_id": 111,
            "rating": 4.5,
            "description": "This game is awesome"
        }
    ]
}

However they may not need all this information, but then again they might. Making calls for everything seems like a bad idea from I/O and performance.

I thought about doing it by specifying include parameter in the requests.

Now for example if you did not specify any includes all you would get back is the following.

{
    "id": 1,
    "title": "Call of Duty Advanced Warfare",
    "release_date": "2014-11-24"
}

However it you want all the information your requests would look something like this.

/api/game/1?include=publishers,developers,platforms,reviews,esrbRating

This way the client has the ability to specify how much information they want. However I'm kind of at a loss the best way to implement this using Spring MVC.

I'm thinking the controller would look something like this.

public @ResponseBody Game getGame(@PathVariable("id") long id, 
    @RequestParam(value = "include", required = false) String include)) {

        // check which include params are present

        // then someone do the filtering?
}

I'm not sure how you would optionally serialize the Game object. Is this even possible. What is the best way to approach this in Spring MVC?

FYI, I am using Spring Boot which includes Jackson for serialization.

Answer

Marlon Bernardes picture Marlon Bernardes · May 31, 2015

Instead of returning a Game object, you could serialize it as as a Map<String, Object>, where the map keys represent the attribute names. So you can add the values to your map based on the include parameter.

@ResponseBody
public Map<String, Object> getGame(@PathVariable("id") long id, String include) {

    Game game = service.loadGame(id);
    // check the `include` parameter and create a map containing only the required attributes
    Map<String, Object> gameMap = service.convertGameToMap(game, include);

    return gameMap;

}

As an example, if you have a Map<String, Object> like this:

gameMap.put("id", game.getId());
gameMap.put("title", game.getTitle());
gameMap.put("publishers", game.getPublishers());

It would be serialized like this:

{
  "id": 1,
  "title": "Call of Duty Advanced Warfare",
  "publishers": [
    {
        "id": 1,
        "name": "Activision"
    }
  ]
}