2015-05-31 4 views
10

Я продумал лучший способ разработки JSON API с использованием Spring MVC. Поскольку мы все знаем, что IO дорого, и поэтому я не хочу, чтобы клиент сделал несколько вызовов API, чтобы получить то, что им нужно. Однако в то же время я не обязательно хочу вернуть кухонную раковину.Возвращаемые переменные возврата Spring Spring

В качестве примера я работал над игровым API, похожим на IMDB, но для видеоигр.

Если бы я вернул все, что связано с игрой, это выглядело бы примерно так.

/апи/игра/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" 
     } 
    ] 
} 

Однако они не могут нуждаться во всей этой информации, но потом они снова могли бы. Выполнение вызовов во всем кажется плохой идеей ввода-вывода и производительности.

Я думал об этом, указав параметр include в запросах.

Теперь, например, если вы не указали, какие из них включают все, что вы получили, это следующее.

{ 
    "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?

FYI, я использую Spring Boot, который включает в себя Jackson для сериализации.

+0

Похоже вы немного преждевременной оптимизации здесь. Есть ли в вашей сущности столько данных, что вам нужно отфильтровать по запросу клиента? Основываясь на том, что вы показали, вы слишком усложняете как клиент, так и сервер и нарушаете RESTfullness вашей службы, не сохраняя при этом много IO. –

+0

В случае с моим примером, я согласен, что это определенно излишне. Давайте просто скажем, например, ради, хотя это возвращает объект Game, приведет к огромному объекту JSON, вы говорите, что было бы лучше сделать/game/1/reviews, а не/game/1? Include = review? – greyfox

+0

Если объект огромен, тогда я бы запросил коллекции из отдельных подзаголовков, потому что накладные расходы на выдачу нескольких запросов были бы незначительными по отношению к общему объему переданных данных. –

ответ

9

Вместо того, чтобы возвращать объект Game, вы можете сериализовать его как 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" 
    } 
    ] 
} 
+0

Вау, это на самом деле простой и очень гибкий способ справиться с этим. – greyfox

+0

Выполнение этого с помощью настраиваемой службы не очень хорошо для меня, есть ли какой-нибудь «весенний» способ сделать это? – EralpB

+0

@ EralpB вы также можете искать, как сериализовать поля на основе их имени (используя Джексон) или использовать что-то вроде GraphQL. Кроме этого, я не уверен, есть ли «весенний» способ добиться того же результата. –

Смежные вопросы