RESTful API: HTTP-глаголы с общими или конкретными URL-адресами?

25

При создании API RESTful следует ли использовать HTTP-глаголы для одного и того же URL-адреса (когда это возможно) или мне следует создавать определенный URL-адрес для каждого действия?

Например:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Или с конкретными URL-адресами, такими как:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item
53777A
источник

Ответы:

46

В вашей последней схеме вы сохраняете глаголы в URL ваших ресурсов. Этого следует избегать, так как для этой цели следует использовать глаголы HTTP. Используйте базовый протокол вместо того, чтобы игнорировать, дублировать или переопределять его.

Просто посмотрите DELETE /item/delete/:id, вы размещаете одну и ту же информацию дважды в одном запросе. Это лишнее и его следует избегать. Лично меня бы это смутило. API действительно поддерживает DELETEзапросы? Что если я добавлю deleteURL-адрес и использую другой HTTP-глагол? Будет ли это соответствовать что-нибудь? Если так, какой из них будет выбран? Как клиент правильно спроектированного API, мне не нужно было задавать такие вопросы.

Может быть, вам это нужно для поддержки клиентов, которые не могут выдавать DELETEили PUTзапрашивать. Если это так, я бы передал эту информацию в заголовок HTTP. Некоторые API используют X-HTTP-Method-Overrideзаголовок для этой конкретной цели (что, я думаю, в любом случае довольно уродливо). Я конечно не буду помещать глаголы в пути все же.

Пойти на

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Что важно для глаголов, так это то, что они уже четко определены в спецификации HTTP, и соблюдение этих правил позволяет вам использовать кеши, прокси и, возможно, другие инструменты, внешние по отношению к вашему приложению, которые понимают семантику HTTP, но не семантику вашего приложения. , Обратите внимание, что причина, по которой вы должны избегать их использования в своих URL-адресах, не в том, что RESTful API требуют читаемых URL-адресов. Речь идет об избежании ненужной двусмысленности.

Более того, RESTful API может отображать эти глаголы (или любое их подмножество) в любой набор семантики приложения, если это не противоречит спецификации HTTP. Например, вполне возможно построить RESTful API , который использует только GET запросы , если все операции , которые она позволяет являются как сейф и идемпотент . Приведенное выше сопоставление является лишь примером, который соответствует вашему варианту использования и соответствует спецификации. Это не обязательно должно быть так.

Помните также, что для действительно RESTful API никогда не требуется, чтобы программист читал обширную документацию по доступным URL-адресам, если вы соблюдаете принцип HATEOAS (гипертекст как движок состояния приложения), который является одним из основных предположений REST . Ссылки могут быть совершенно непонятными для человека, если клиентское приложение может их понять и использовать для определения возможных переходов состояния приложения.

toniedzwiedz
источник
4
В отсутствие PUTи DELETEя бы предпочел добавить его к пути, а не дифференцировать его с помощью строки запроса. Это не модификация строки запроса для существующей операции; это отдельная операция.
Роберт Харви
4
@RobertHarvey в этом случае, я бы все равно назвал это хаком. Как вы говорите, это операция, и это не то, что я бы указал на пути при разработке API, целью которого является RESTful. Размещение его в строке запроса кажется менее инвазивным. Это предотвращает кеширование, но я не думаю, что ответы на запросы такого рода должны кэшироваться в любом случае. Это также позволяет потребителю API легко указывать метод без разбора или создания URL-адреса. В идеале, действительно RESTful API должен предоставлять гиперссылки, не требуя, чтобы клиенты сами создавали URL-адреса.
toniedzwiedz
Если у вас нет всех глаголов, это не совсем RESTful, не так ли?
Роберт Харви
@RobertHarvey правда, но я отношусь к ним как к запасному варианту, а не как к задуманному проекту. Я полагаю, что API должен поддерживать реальные методы HTTP, и если какой-либо клиент не может реализовать их по какой-либо причине, он может просто заменить свое использование этими параметрами запроса. Прокси-сервер может даже захватывать их на лету и преобразовывать запросы в запросы с использованием подлинных HTTP-глаголов, поэтому серверу даже не нужно заботиться. Немногие API действительно RESTful. Когда дело доходит до общих веб-API, это действительно вопрос вкуса. Лично я бы пошел на чистые URL. Проще понять ИМХО.
toniedzwiedz
1
@RobertHarvey, как объяснено, вряд ли предназначен для их использования. Я считаю, что это меньшее из двух зол, когда нужно преодолеть клиентские ограничения. Я помню, что читал документацию по такому API, но мне придется сделать некоторые раскопки в истории / закладках моего браузера, чтобы найти его. Теперь, когда я думаю об этом, заголовок может быть лучше в этом случае. Вы бы согласились?
Тонедзведз
14

Первый.

URI / URL - это идентификатор ресурса (подсказка в названии: универсальный идентификатор ресурса). Согласно первому соглашению, ресурс, о котором говорится, когда вы выполняете «GET / user / 123», и ресурс, о котором говорится, когда вы делаете «DELETE / user / 123», - это явно один и тот же ресурс, поскольку они имеют одинаковый URL.

Со вторым соглашением вы не можете быть уверены, что «GET / user / 123» и «DELETE / user / delete / 123» на самом деле являются одним и тем же ресурсом, и кажется, что это означает, что вы удаляете связанный ресурс, а не ресурс сам по себе, так что было бы довольно удивительно, что удаление на /user/delete/123самом деле удаляет /user/123. Если все операции выполняются с разными URL-адресами, URI больше не работает в качестве идентификатора ресурса.

Когда вы говорите DELETE /user/123, вы говорите «удалить» запись пользователя с идентификатором 123 ». Хотя, если вы говорите DELETE /user/delete/123, то, что вы, похоже, намекаете, это «удалить» запись удаления пользователя с идентификатором 123 », что, вероятно, не то, что вы хотите сказать. И даже если в этой ситуации вы используете более правильный глагол: «POST / user / delete / 123», который говорит: «выполнить операцию, связанную с« пользовательским удалителем с идентификатором 123 »», это все еще окольный способ сказать «удалить запись». (это похоже на глагол в английском языке).

Один из способов думать об URL-адресе - это рассматривать его как указатели на объекты и ресурсы как объекты в объектно-ориентированном программировании. Когда вы делаете GET /user/123, DELETE /user/123вы можете думать , думать о них , как методы в объекте: [/user/123].get(), [/user/123].delete()где [], как указатель разыменования оператора , но для URL - адресов (если вы знаете язык, есть указатели). Одним из основополагающих принципов REST является унифицированный интерфейс, то есть наличие небольшого и ограниченного набора глаголов / методов, который работает для всего в обширной сети ресурсов / объектов.

Поэтому первый лучше.

PS: конечно, это смотрит на REST в чистом виде. Иногда практичность превосходит чистоту, и вам нужно идти на уступки для умственно отсталых клиентов или структуры, которая затрудняет правильное REST.

Ли Райан
источник
+1 для примера ООП :)
53777A
6

(извините, я впервые пропустил / edit / и / delete / in (2) ...)

Идея URI заключается в том, что это идентификатор адресуемого ресурса , а не вызов метода . Таким образом, URI должен указывать на конкретный ресурс. И если вы уважаете URI, вы всегда должны получать один и тот же ресурс.

То есть вы должны думать об URI так же, как и о первичном ключе строки в базе данных. Он однозначно идентифицирует что-то: универсальный идентификатор ресурса.

Так что, используете ли вы множественное или единственное число, URI должен быть идентификатором, а не вызовом . То, что вы пытаетесь сделать, относится к методу, а именно: GET (получить), PUT (создать / обновить), DELETE (удалить) или POST (все остальное).

Так что "/ item / delete / 123" нарушает REST, потому что он не указывает на ресурс, это скорее вызов метода.

(Также, семантически, вы должны иметь возможность ПОЛУЧИТЬ URI, решить, что он устарел, а затем УДАЛИТЬ тот же URI - потому что это идентификатор. Если GET URI не имеет "/ delete /", а DELETE делает, тогда это идет вразрез с семантикой HTTP. Вы транслируете 2 или более URI на ресурс, где будет делать 1).

Обман заключается в следующем: нет точного определения того, что является и не является ресурсом, поэтому обычная хитрость в REST - определить «существительное обработки» и указать на него URI. Это в значительной степени игра в слова, но она удовлетворяет семантике.

Так что, если, например, вы действительно не можете использовать это по какой-то причине:

DELETE /items/123

Вы могли бы заявить миру, что у вас есть ресурс обработки "deletor" и использовать

POST /items/deletor  { id: 123 }

Теперь это выглядит как RPC (удаленный вызов процедур), но оно проходит через огромную лазейку в предложении спецификации POST «обработка данных», названном в спецификации HTTP.

Тем не менее, выполнение этого является исключительным, и если вы можете использовать общий PUT для создания / обновления, DELETE для удаления и POST для добавления, создания и всего остального, то вам следует это сделать , поскольку это более стандартное использование HTTP. Но если у вас есть хитрый случай, такой как «commit» или «publish» или «redact», тогда случай использования существительного процессора удовлетворяет пуристам REST и все же дает вам семантику, которая вам нужна.

обкрадывать
источник