HTTP POST с параметрами URL-запроса - хорошая идея или нет?
Я разрабатываю API для работы с HTTP, и мне интересно, если использовать команду HTTP POST, но только с параметрами URL-запроса и без тела запроса, это хороший путь.
Соображения:
- "Хороший веб-дизайн" требует отправки неидемпотентных действий через POST. Это неидемпотентное действие.
- Это приложение легче разрабатывать и отлаживать, когда параметры запроса присутствуют в URL.
- API не предназначен для широкого использования.
- Похоже, что выполнение запроса POST без тела потребует немного больше работы, например,
Content-Length: 0
заголовок должен быть явно добавлен. - Мне также кажется, что POST без тела немного противоречит ожиданиям большинства разработчиков и HTTP-фреймворков.
Есть ли еще какие-нибудь подводные камни или преимущества при отправке параметров в запросе POST через URL-запрос, а не в теле запроса?
Редактировать: причина, по которой это рассматривается, заключается в том, что операции не являются идемпотентными и имеют побочные эффекты, кроме извлечения. Смотрите спецификации HTTP:
В частности, было установлено, что методы GET и HEAD НЕ ДОЛЖНЫ иметь значение выполнения действия, отличного от извлечения. Эти методы следует считать "безопасными". Это позволяет пользовательским агентам представлять другие методы, такие как POST, PUT и DELETE, особым образом, чтобы пользователь знал о том, что запрашивается небезопасное действие.
...
Методы также могут иметь свойство "идемпотентности", заключающееся в том, что (кроме ошибок с ошибками или истечением срока действия) побочные эффекты от N > 0 идентичных запросов такие же, как и для одного запроса. Методы GET, HEAD, PUT и DELETE разделяют это свойство. Кроме того, методы OPTIONS и TRACE НЕ ДОЛЖНЫ иметь побочных эффектов, и поэтому являются по своей сути идемпотентными.
8 ответов
Если ваше действие не идемпотентно, вы ДОЛЖНЫ использовать POST
, Если вы этого не сделаете, вы просто напрашиваетесь на неприятности в будущем. GET
, PUT
а также DELETE
методы должны быть идемпотентными. Представьте, что произойдет в вашем приложении, если клиент предварительно выбирает все возможные GET
запрос на вашу услугу - если это вызовет побочные эффекты, видимые для клиента, значит, что-то не так.
Я согласен, что отправка POST
со строкой запроса, но без тела кажется странным, но я думаю, что это может быть уместно в некоторых ситуациях.
Думайте о части запроса URL как о команде ресурса, чтобы ограничить область действия текущего запроса. Как правило, строки запроса используются для сортировки или фильтрации GET
запрос (как ?page=1&sort=title
) но я думаю, что это имеет смысл на POST
также ограничить сферу (возможно, как ?action=delete&id=5
).
Все правы: придерживайтесь POST для неидемпотентных запросов.
Как насчет использования строки запроса URI и содержимого запроса? Ну, это действительный HTTP (см. Примечание 1), так почему бы и нет!
Это также совершенно логично: URL-адреса, включая часть строки запроса, предназначены для поиска ресурсов. Принимая во внимание, что глаголы метода HTTP (POST - и его необязательное содержимое запроса) предназначены для указания действий или действий с ресурсами. Это должны быть ортогональные проблемы. (Но они не являются красиво ортогональными проблемами для особого случая ContentType=application/x-www-form-urlencoded, см. Примечание 2 ниже.)
Примечание 1. В спецификации HTTP (1.1) не указано, что параметры и содержимое запроса являются взаимоисключающими для HTTP-сервера, который принимает запросы POST или PUT. Таким образом, любой сервер может принять оба варианта. То есть, если вы пишете сервер, ничто не мешает вам принять и то и другое (кроме, может быть, негибкой структуры). Обычно сервер может интерпретировать строки запроса в соответствии с любыми правилами. Он может даже интерпретировать их с помощью условной логики, которая также относится к другим заголовкам, таким как Content-Type, что приводит к примечанию 2:
Примечание 2: если веб-браузер является основным способом доступа пользователей к вашему веб-приложению, а application / x-www-form-urlencoded является типом контента, который они публикуют, то вы должны следовать правилам для этого типа контента. А правила для application / x-www-form-urlencoded гораздо более специфичны (и, честно говоря, необычны): в этом случае вы должны интерпретировать URI как набор параметров, а не местоположение ресурса. [Это та же самая полезность, которую поднял Powerlord; что может быть трудно использовать веб-формы для размещения контента на вашем сервере. Просто объяснил немного по-другому.]
Примечание 3: для чего изначально нужны строки запроса? RFC 3986 определяет строки HTTP-запроса как часть URI, которая работает как неиерархический способ поиска ресурса.
Если читатели, задающие этот вопрос, захотят спросить, что такое хорошая архитектура RESTful: шаблон архитектуры RESTful не требует, чтобы схемы URI работали определенным образом. Архитектура RESTful связана с другими свойствами системы, такими как кэшируемость ресурсов, структура самих ресурсов (их поведение, возможности и представления), а также удовлетворяется ли идемпотентность. Или, другими словами, достижение дизайна, который очень совместим с протоколом HTTP и его набором глаголов метода HTTP.:-) (Другими словами, архитектура RESTful не очень хорошо понимает, как расположены ресурсы.)
Последнее замечание: иногда параметры запроса используются для других целей, которые не являются ни локализацией ресурсов, ни кодированием контента. Вы когда-нибудь видели параметр запроса, такой как "PUT=true" или "POST=true"? Это обходные пути для браузеров, которые не позволяют использовать методы PUT и POST. Хотя такие параметры рассматриваются как часть строки запроса URL (на проводе), я утверждаю, что они не являются частью запроса URL по сути.
Вы хотите причины? Вот один из них:
Веб-форму нельзя использовать для отправки запроса на страницу, которая использует сочетание GET и POST. Если вы установите метод формы GET, все параметры будут в строке запроса. Если вы установите метод формы POST, все параметры будут в теле запроса.
Источник: стандарт HTML 4.01, раздел 17.13.
С программной точки зрения для клиента он упаковывает параметры и добавляет их в URL и проводит POST против GET. На стороне сервера он оценивает входящие параметры из строки запроса вместо отправленных байтов. По сути, это стирка.
Возможные преимущества и недостатки могут быть связаны с тем, как конкретные клиентские платформы работают с процедурами POST и GET в их сетевом стеке, а также с тем, как веб-сервер обрабатывает эти запросы. В зависимости от вашей реализации, один подход может быть более эффективным, чем другой. Знание этого будет направлять ваше решение здесь.
Тем не менее, с точки зрения программиста, я предпочитаю разрешать либо POST со всеми параметрами в теле, либо GET со всеми параметрами в URL, и явно игнорировать параметры URL с любым запросом POST. Это позволяет избежать путаницы.
Я думаю, что все еще может быть достаточно RESTful иметь аргументы запроса, которые идентифицируют ресурс в URL, сохраняя при этом полезную нагрузку контента, ограниченную телом POST. Казалось бы, это разделяет соображения "Что я посылаю?" против "Кому я отправляю это?"
В лагере REST есть несколько руководящих принципов, которые мы можем использовать для стандартизации способа использования HTTP-глаголов. Это полезно при создании RESTful API, как вы делаете.
В двух словах: GET должен быть только для чтения, т.е. не должен влиять на состояние сервера. POST используется для создания ресурса на сервере. PUT используется для обновления или создания ресурса. DELETE используется для удаления ресурса.
Другими словами, если ваше действие API изменяет состояние сервера, REST советует нам использовать POST/PUT/DELETE, но не GET.
Пользовательские агенты обычно понимают, что выполнение нескольких POST - это плохо, и предупреждают об этом, потому что цель POST - изменить состояние сервера (например, оплатить товары при оформлении заказа), и вы, вероятно, не хотите делать это дважды!
Сравните с GET, который вы можете делать часто, как вам нравится (идемпотент).
Было бы хорошо использовать параметры запроса в конечной точке POST, если они относятся к уже существующему ресурсу.
Например:
POST /user_settings?user_id=4
{
"use_safe_mode": 1
}
В приведенном выше POST есть параметр запроса, относящийся к существующему ресурсу. Параметр body определяет новый создаваемый ресурс.
Отредактировано:
Я предпочитаю, чтобы путь к конечной точке указывал непосредственно на уже существующий ресурс, например:
POST /user_settings/4
{
...
}
Причина тройная:
- Я считаю, что он лучше читается, так как параметры запроса названы, как "user_id" в приведенном выше примере, вместо "4".
- Обычно существует также конечная точка GET для получения ресурса. В этом случае путь к конечной точке и параметры запроса будут одинаковыми, и мне нравится эта симметрия.
- Я считаю, что вложение может стать громоздким и трудным для чтения, если для определения уже существующего ресурса требуется несколько параметров:
POST /user_settings/{user_id}/{which_settings_id}/{xyz}/{abc}/ ...
{
...
}
Я согласен - возможно, безопаснее использовать запрос GET, если вы просто передаете данные в URL, а не в теле. Смотрите этот похожий вопрос для некоторых дополнительных взглядов на всю концепцию POST+GET.