22

我一直在思考使用 Spring MVC 设计 JSON API 的最佳方法。众所周知,IO 很昂贵,因此我不想让客户端进行多次 API 调用来获得他们需要的东西。但与此同时,我不一定要归还厨房水槽。

例如,我正在开发一个类似于 IMDB 的游戏 API,但用于视频游戏。

如果我返回与 Game 相关的所有内容,它会看起来像这样。

/api/游戏/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"
        }
    ]
}

然而,他们可能不需要所有这些信息,但他们可能再次需要。从 I/O 和性能来看,调用所有内容似乎是个坏主意。

我考虑通过在请求中指定包含参数来做到这一点。

现在例如,如果您没有指定任何包含,那么您将得到以下内容。

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

但是,您希望您的请求的所有信息看起来像这样。

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

这样,客户就可以指定他们想要多少信息。但是,我对使用 Spring MVC 实现这一点的最佳方法感到不知所措。

我在想控制器看起来像这样。

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?
}

我不确定您将如何选择序列化 Game 对象。这甚至可能吗。在 Spring MVC 中解决这个问题的最佳方法是什么?

仅供参考,我正在使用包含 Jackson 的 Spring Boot 进行序列化。

4

4 回答 4

19

Game您可以将其序列化为 a ,而不是返回一个对象,Map<String, Object>其中映射键表示属性名称。因此,您可以根据include参数将值添加到地图中。

@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;

}

例如,如果你有Map<String, Object>这样的:

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

它将像这样序列化:

{
  "id": 1,
  "title": "Call of Duty Advanced Warfare",
  "publishers": [
    {
        "id": 1,
        "name": "Activision"
    }
  ]
}
于 2015-05-31T15:20:47.713 回答
5

意识到我的答案来得太晚了:我建议看看Projections.

你要问的是预测是关于什么的。

既然你问的是春天,我会试试这个: https ://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts

于 2018-10-14T21:21:28.230 回答
1

这可以通过 Spring 完成Projections。也适用于 Kotlin。看看这里:https ://www.baeldung.com/spring-data-jpa-projections

于 2020-06-02T20:54:51.033 回答
0

解决方案 1:将 @JsonIgnore 添加到您不想包含在 API 响应中的变量(在模型中)

@JsonIgnore
    private Set<Student> students;

解决方案 2:删除您不想包含的变量的 getter。

如果您在其他地方需要它们,请为 getter 使用不同的格式,以便 spring 不知道它。

于 2021-08-28T08:25:49.617 回答