Поиск по идентификатору и имени пользователя в веб-сервисах RESTful - PullRequest
0 голосов
/ 25 января 2020

Я запутался, как лучший выбор для одноэлементного ресурса. Я хочу иметь конечную точку с идентификатором пользователя:

/users/{id}

Но я также хочу username в качестве параметра:

/users/{username}

Я использую Spring MVC (через Spring Boot), и он говорит, что 2 конечные точки находятся в конфликте. Поэтому я решил первым для получения синглтон ресурса для пользователей. Но чтобы клиент все еще использовал имя пользователя в качестве параметра, я добавил имя пользователя в качестве параметра запроса для ресурса сбора пользователей:

/users?username=<username>

В моих repository и service слои, возвращаемое значение равно Optional<User>, т.е. это либо empty, либо one user результат. Но в controller я обертываю это в list, либо пустое, либо единичное, чтобы согласовать его с возвращением /users в виде списка.

Это разумный дизайн? или есть лучший дизайн? Спасибо.

Ответы [ 3 ]

2 голосов
/ 25 января 2020

По сути, у вас есть две опции:

  • У вас есть две отдельные конечные точки, например, /users/byId/{id} и /users/byName/{username}, каждая из которых сопоставлена ​​с различным методом контроллера
class UserController {
    @GetMapping("/byId/{id}")
    Optional<User> findById(@PathVariable("id") String id){
       ...
    }

    @GetMapping("/byName/{username}")
    Optional<User> findByUsername(@PathVariable("username") String username){
       ...
    }
}
  • Или одна конечная точка /users/{id-or-name}, и вы делаете запрос объединения, так что: findAllByIdOrName()

Я склонен сказать, что это чище и эффективнее делать это в отдельности конечные точки.


Редактировать

Соглашение RESTful будет означать, что у вас есть 2 конечные точки:

    @GetMapping("/{id}")
    Optional<User> findById(@PathVariable("id") String id){
       ...
    }

    @GetMapping("/")
    List<User> findUsers(@RequestParam("username") String username){
       // if username is not empty, filter users
       // we could also filter with other user properties according to specs
       ...
    }

Все что угодно, кроме этого, будет уже отклоняется от условностей.

1 голос
/ 25 января 2020

Я постараюсь ответить на ваш вопрос подробно, хотя об этом много написано в inte rnet.

Поскольку я думаю, что это менее актуальная тема c (мое мнение), тем не менее, вы должны попытаться помешать клиентам собирать URI со строковыми операциями, потому что для этого клиенты должны иметь глубокие знания о внутренностях сервера.

Тем не менее, элегантные схемы URI часто являются признаком разумной структуры ресурсов. Когда URI хорошо спроектированы, это облегчает жизнь разработчику, который хочет использовать REST API; клиенты оценят URI, которые легко запомнить. Возможность редактировать URI вручную (например, обрезая некоторые из них после "/") является определенным преимуществом.

Как вы уже видели: URI идентифицируют ресурсы. Ресурсы - это «вещи», существительные, а не действия или глаголы! Если вы сделали коррозийный дизайн ресурса, соответствующие имена обычно появляются сами по себе. Однако я хотел бы привести здесь пример, который может указывать на проблему из-за ее структуры или названия.

http://example.com/customers/create?name=XYZ

Здесь произошла небольшая путаница: глаголу «создать» не место в URI, и структура предполагает, что действие идентифицируется по URI, а не по глаголу HTTP.

Ваши URI должны содержать существительные. Для URI, которые идентифицируют ресурсы по назначению, вы также можете легко представить, как работают различные методы HTTP: PUT обновляет, удаляет DELETE, GET извлекает информацию. Вот пример того, как вышеуказанный URI выглядел бы лучше:

POST http://example.com/customers/

Очевидным правилом для прозрачного и понятного дизайна URI является отображение иерархической структуры. связи со структурой элемента пути URI. Пример: http://example.com/organization/it/support/networks Так что, если вы разработчик сервера и, следовательно, разработчик URI, вы должны убедиться, что GET для http://example.com/organization/it/support также возвращает полезный результат , В вашем клиенте вы не должны делать такие предположения в любом случае! Это существенно способствует стабильности распределенной системы и также известно как принцип устойчивости или закон Постеля

Для разработки URI для FIlter они имеют различные возможности. Наиболее очевидным является использование параметров запроса. Для списка клиентов в разделе / ​​customer, которые вы хотите отфильтровать для всех клиентов из Германии, пример URI может выглядеть следующим образом:

http://example.com/customers?country=Germany Несколько компонентов запроса могут быть связаны с «&»:

http://example.com/customers?country=Germany&year=2020

Здесь также они соответствуют ожиданиям пользователей с таким дизайном URI. Если строка запроса опущена (? Страна = Германия и год = 2020), пользователи попадают в нефильтрованный список всех клиентов.

Важно иметь стабильный Uris, крутой URIS не меняется (Тим Бернерс-Ли).

Конечно, я мог бы рассказать вам кое-что об HTTP-глаголах или о том, как использовать протокол, отличный от HTTP, или о том, как загружать файлы в чанках и т. Д. c. но я оставляю это открытым для самостоятельной работы =).

Чтобы ответить на ваш вопрос: дизайн не следует переоценивать; это должно быть полезно, но не идеально (чего невозможно достичь в любом случае).

0 голосов
/ 25 января 2020

Нельзя использовать одно и то же имя конечной точки для одного конкретного сопоставления. Здесь вы используете одно и то же имя конечной точки для обоих @GetMapping. Если вы хотите сделать это, вы должны использовать параметры для этого.

class UserController {
   @GetMapping("/users")
   Optional<User> findById(@RequestParam(required = false) String id, @RequestParam(required = false) String username){
      if (!username.equals("")) {
         // response using username
      }
      if (!id.equals("")) {
         // response using id
      }
   }
}
...