Операции без CRUD в службе RESTful

106

Что такое RESTful-способ добавления не-CRUD операций в службу RESTful? Скажем, у меня есть служба, которая разрешает CRUD доступ к таким записям:

GET /api/car/123           <- Returns information for the Car object with ID 123
POST /api/car              <- Creates a new car (with properties in the request)
PUT /api/car/123           <- Updates car 123 (with properties in the request)
DELETE /api/car/123        <- Deletes car 123    
POST /api/car/123/wheel/   <- Creates a wheel and associates it to car 123

Если я хочу изменить цвет автомобиля, я бы просто POST /api/car/123добавил переменную POST для нового цвета.

Но предположим, что я хочу купить автомобиль, и эта операция более сложна, чем простое обновление свойства «собственный автомобиль» записи «пользователь». Является ли RESTful просто делать что-то вроде POST /api/car/123/purchase, где «покупка», по сути, является именем метода? Или мне следует использовать собственный HTTP-глагол, например, PURCHASEвместо POST?

Или операции, не относящиеся к CRUD, полностью выходят за рамки REST?

MikeWyatt
источник
5
Если вы меняете цвет автомобиля, было бы лучше использовать PATCH /api/car/123и отправить параметр цвета ИЛИ использовать PUT /api/car/123и отправить весь объект автомобиля. POST будет означать, что вы создаете новую машину и, вероятно, никогда не должны включать идентификатор в конце URL-адреса
RonnyKnoxville

Ответы:

65

Подумайте о покупке как о бизнес-субъекте или как о ресурсе в словаре RESTful. При этом покупка фактически создает новый ресурс. Так:

POST /api/purchase

разместит новый заказ. На детали (пользователь, автомобиль и т. Д.) Следует ссылаться по идентификатору (или URI) внутри содержимого, отправляемого на этот адрес.

Неважно, что заказ автомобиля - это не просто ВСТАВИТЬ в базу данных. На самом деле REST не предназначен для отображения таблиц базы данных как операций CRUD. С логической точки зрения вы создаете заказ (покупку), но серверная сторона может выполнять столько шагов обработки, сколько захочет.

Вы даже можете еще больше злоупотреблять протоколом HTTP. Используйте Locationзаголовок, чтобы вернуть ссылку на вновь созданный заказ, тщательно выбирайте коды ответа HTTP, чтобы информировать пользователей о проблемах (на стороне сервера или на стороне клиента) и т. Д.

Томаш Нуркевич
источник
3
REST - это управление состоянием ресурсов, и каждая бизнес-операция должна быть сопоставлена ​​с операциями CRUD состояния. Если вам нужна жесткая семантика бизнес-операций, вам придется пойти по пути SOAP (SOAP - это фактически передача сообщений, но обычно организована в виде операций запрос-ответ).
Tomasz Nurkiewicz
23
Дизайн «покупка как ресурс» выглядит аккуратно. Что, если ресурс - это "пиво" .. и я хочу, чтобы сервер выпил его .. (это было для меня, я обязательно ПОЛУЧИТЕ;)) .. следует ли рассматривать "действие выпивки" как ресурс ?! .. или это "пить пиво" тяжелая деловая операция ?! А если серьезно, то RESTful-дизайн рассматривает действия как ресурсы?! ..
Myobis
2
Как бы вы выставили «одобрить заказ на покупку» через службу REST? Я думаю, что @TomaszNurkiewicz прав в том, что все, что нельзя аккуратно сделать с помощью CRUD, потребует семантики операций, предоставляемой SOAP. Если только «утверждение заказа на поставку» не является отдельной моделью / сущностью. Например, POST / po-согласование (с деталями PO в запросе).
mydoghasworms
2
С точки зрения клиента REST «одобрить заказ на покупку» должно быть просто очередным обновлением заказа. Например, измените «Утверждено» на «Истина» и отправьте обновление на сервер. Серверу, вероятно, потребуется выполнить несколько проверок и, возможно, потребуется обновить / создать множество других ресурсов. Но это проблема серверов, и она не должна быть видна клиенту.
AVee
2
@antinome: «Предположим, что клиент кое-что из этого знает», если это так, вы не используете REST (хотя это может быть действительное, разумное программное обеспечение!). REST был разработан, чтобы иметь возможность создавать клиентов, которые не знают этого, для создания клиентов, которые все еще работают, если поведение сервера изменится. То, что вы пытаетесь сделать, это классический RPC, вам нужно либо пересмотреть свой подход, чтобы он соответствовал REST, либо согласиться с тем, что вы выполняете RPC и используете протокол, предназначенный для RPC, например SOAP. REST очень старается не быть RPC, поэтому он никогда не подойдет, когда вам нужен / нужен RPC.
AVee
15

Насколько я понимаю, способ RESTful заключается в том, что вам не нужны новые HTTP-глаголы, где-то есть существительное, которое будет означать то, что вам нужно делать.

Купите машину? Ну разве не так

POST /api/order
джна
источник
2
Разве PUT не используется для обновления ресурсов, поскольку он идемпотентен? Это означает, что вы можете вызывать его столько раз, сколько хотите, но важен только первый / последний вызов. POST, с другой стороны, используется для создания ресурсов, и его двойной вызов фактически должен создать два.
Tomasz Nurkiewicz
1
@ Томас, да, опечатка. Однако принцип важен, мы имеем дело с чем-то новым, с порядком, нет необходимости в новом глаголе.
djna
5

На самом деле вы создаете заказ. Поэтому добавьте еще один ресурс для заказа и публикации и поместите его во время процесса заказа.

Думайте о ресурсах, а не о вызовах методов.

Чтобы завершить заказ, вы, вероятно, выполните POST / api / order // complete или что-то подобное.

Эндрю Котманн
источник
3

Я считаю, что REST API помогают гораздо больше, чем просто предоставляют семантику. Поэтому нельзя выбрать стиль RPC только из-за некоторых вызовов, которые кажутся более понятными в стиле работы RPC. Пример - API карт Google для поиска направлений между двумя местами. Выглядит так: http://maps.googleapis.com/maps/api/directions/json?origin=Jakkur&destination=Hebbal

Они могли бы назвать это «findDirections» (глагол) и рассматривать его как операцию. Вместо этого они сделали «направление» (существительное) как ресурс и рассматривали поиск направлений как запрос к ресурсу направлений (хотя внутри не могло быть реального ресурса, называемого направлением, и это могло быть реализовано бизнес-логикой для поиска направлений на основе параметров).

Марути
источник
Это плохой пример. В этом случае направления (все возможные направления, бесконечное количество) - это ресурс, а параметры - просто фильтры. Но вы не можете совершить «покупку» с этим, поскольку фильтры имеют смысл только при операциях получения, а размещение заказа или отмена - это операции, которые изменяют данные
Ценг
2
покупка будет POST to / order с json в теле, чтобы указать, что заказ создан. cancel будет PUT to / order с json, несущим изменение состояния заказа, чтобы указать, что это идемпотентное обновление. Мне все еще предстоит столкнуться с операцией, которую нельзя выразить в формате ресурса. Так что очень хотелось бы увидеть такой пример
Марути