Существует еще одна аналогичная question, но обсуждение отклонилось от проблемы, с которой я сталкиваюсь.
Скажем, у меня есть система, которая занимается отчетами о расходах (ER). Вы можете создавать и редактировать их, добавлять вложения и одобрять/отклонять их.
Отчет о расходах может выглядеть следующим образом:
GET /er/1
=>
{"title": "Trip to NY", "totalcost": "400 USD",
"comments": [
"john: Please add the total cost",
"mike: done, can you approve it now?"
],
"approvals": [
{"john": "Pending"}, {"finance-group": "Pending"}]
}
Это выглядит хорошо, не так ли? Вот как выглядит документ отчета о расходах.
Если вы хотите его обновить, вы можете сделать это:
POST /er/1
{"title": "Trip to NY 2010"}
Если вы хотите его одобрить, вы можете сделать это:
POST /er/1/approval
{"approved": true}
Но что, если вы хотите обновить отчет и одобрить его одновременно? Как мы это делаем? Если вы только хотели одобрить, то сделать POST
для чего-то вроде /er/1/approval
имеет смысл.
Мы могли бы поместить флаг в URL, POST /er/1?approve=1
и отправить данные в виде тела, но этот флаг не отображается RESTful.
Мы также могли бы отправить специальное поле, но это тоже немного взломано. Если бы мы это сделали, то почему бы не отправить данные с такими атрибутами, как set_title
или add_to_cost
?
Мы могли бы создать новый ресурс для обновления и утверждения, но (1) я не могу придумать, как назвать его без глаголов, и (2) не представляется правильным назвать ресурс на основе того, какие действия могут (что произойдет, если мы добавим больше действий?)
У нас может быть заголовок X-Approve: True | False, но заголовки кажутся неправильным инструментом для работы. Также было бы трудно получить заголовки набора без использования javascript в браузере.
Мы могли бы использовать пользовательский медиа-тип application/approve+yes
, но это не лучше, чем создание нового ресурса.
Мы могли бы создать временный URL-адрес "пакетных операций", /er/1/batch/A
. Затем клиент отправляет несколько запросов, возможно, POST /er/1/batch/A
для обновления, затем POST /er/1/batch/A/approval
для утверждения, а затем POST /er/1/batch/A/status
, чтобы завершить пакет. На бэкэнд сервер останавливает все пакетные запросы где-то, а затем обрабатывает их в одной и той же бэкэнд-транзакции, когда получает запрос "окончательная пакетная обработка". Недостатком этого является, очевидно, то, что он вводит большую сложность.
Итак, какой хороший, общий способ решить проблему выполнения нескольких действий в одном запросе? Потому что его легко представить дополнительные действия, которые могут быть выполнены по одному и тому же запросу:
- Подавлять или отправлять уведомления (на электронную почту, чат, другую систему, что угодно)
- Отменить некоторые проверки (максимальная стоимость, имена участников обеда)
- Рабочий процесс триггера, который не имеет представления в документе.
Это также проблема производительности. HTTP-вызовы попадают в сеть (что может быть проблемой, если у вас высокая латентность или плохое соединение), поэтому чем меньше их вы можете сделать, тем лучше.