Комментарии 13
"Сам протокол" не "не запрещает" GET с Body. До 2014-го года RFC2616 (HTTP 1.1 Spec) декларировал что GET запрос может иметь Body, но этот Body должен быть проигнорирован сервером. Если сервер как-то обрабатывает Body, и его ответ меняется в зависимости от передаваемых в нём данных - он нарушает RFC.
После 2014-го это убрали, и заменили на "body GET запроса не имеет семантики и может вызвать реджект запроса".
Это собственно довольно заметно становится в последнее время. Всё больше библиотек просто зафейлит и исходящий GET реквест с Body (javascript fetch, Unity из тех с чем я работал), и входящий GET c Body (вот на вскидку не помню...).
Так что GET с Body был прикольным костылём, но сейчас уже пора писать прямо что это не просто плохая практика, а что так вообще делать нельзя.
На редкость грамотный и доходчивый материал, спасибо! Чистый бриллиант на фоне потока наскоро слепленных постов. Хочется всё бросить и иди к вам работать;) А по теме добавить нечего, всё верно сказано. К сожалению, часто куча комментариев и несправедливое внимание достаётся пустопорожним статьям. Успехов вам и всего наилучшего!
Спасибо за статью, очень структурированно, буду рекомендовать коллегам. Давненько мне не хотелось оставить себе конспект статьи Хабра, моё почтение.
а не проще ли разбить запрос на два изначально? будет два гета: /products/{productId}
и /products?someShit=string
. я понимаю, что всё исходит из требований к данным, которые нужно поставлять на клиент, макетам и флоу юзера. но, таким образом, мы сможем минимизировать кол-во информации, которую возвращаем в общем запросе, оставив только базовую инфу. а все остальное, соответственно, выплюнем уже в запросе по конкретному обьекту, и можем туда напихать всего и побольше. ведь, если мы говорим о масштабировании, когда у нас будут разрастаться как и кол-во обьектов, так и информации о них, то неприлично, с точки зрения UX проектировать систему так, чтоб на пользователя вываливался поток информации. значит, система должна проектироваться изначально с расчетом на получение как части данных, так и полным набором, но по конкретному обьекту.
вопрос в следующем, если у нас изначально (если судить по примерам выше) обьект имеет большое (ну допустим больше 10) кол-во параметров, которые нужно возвращать, не проще ли изначально придерживаться плана с двумя гетами и обьяснить дизайнерам, продуктам и прочим, что так мы вместо одного монструозного запроса получим два поменьше, и это облегчит жизнь юзера, и накрутить будет проще новой логики по типу валидации доступов на каждый из них? я рассуждаю, с точки зрения, что дизайнеры и продукты тоже люди, и ошибаться могут, и если из-за их требований приходится создавать сложные запросы, не проще ли убедить их, что они придумали говно, и ты со стороны архитектора апихи видишь, что приходиться выкручиваться и делать один сложный запрос вместо двух попроще?
да, в случае с табличными представлениями данных, мой вопрос выше не имеет смысла, я знаю)
Если нужно вернуть результат по одному товару и список товаров, конечно нужно делать /products/{productId}
и /products?someShit=string
соответственно
Но в ресурсе /products и /products/{productId}
неправильно возвращать список заявок - ресурс ведь про товар, а не про заявки (в статье ведь рассматривается кейс с заявками)
Можно было бы сделать /products/{productId}/requests
- заявки по одному товару и /requests
- все заявки
Я как раз об этом пишу. Если бы было сильное различие по бизнесу, стоит разделить, например:
пользователь может видеть заявки по одному товару, но не имеет доступ ко всем заявкам всех пользователей
на экране 1 в заявках по товару мы возвращаем 5 свойств для отображения каждой заявки, а на экране 2 со всеми заявками - по 20 + еще интеграцией что то получаем (тяжеловесная доп. логика)
Но если по бизнес-смыслу (не по требованиям, а именно по смыслу) - это все таки только фильтр - можно и объединить, как я сделала. Так прокатывает конечно не всегда, но можно попробовать в таком поупражняться.
будет два гета:
/products/{productId}
и/products?someShit=string
. я понимаю, что всё исходит из требований к данным, которые нужно поставлять на клиент, макетам и флоу юзера. но, таким образом, мы сможем минимизировать кол-во информации, которую возвращаем в общем запросе, оставив только базовую инфу
если говорить именно про методы на список заявок - по одному товару или по нескольким товарам, то вряд ли. И так и там будет массив с почти одинаковым количеством свойств. Если и там и там будет пагинация (а ее уже только стажеры забывают прикрутить) - то везде будет возвращаться первая десятка например. Одинаковое количество информации получится
Мне больше нравится такой вариант, особенно, если требуется многоуровневая сортировка
/api/users?sort[name]=ASC&sort[email]=DESC&filter[verified]=true
Хм.. А теперь объясните мне понятно, почему body в GET запросе это плохо?
На мой взгляд как раз портянка параметров запроса в url и является злом. Как минимум это просто не красиво визуально.
Ну и json в body запроса get весьма органичен, мы в json прекрасно объясняем бэкенду, что мы от него хотим получить. json структурирован, он прост и понятен, многомерен в отличии от плоского урла. В урле стоит указывать в каком виде это мы хотим получить: другой json, html или pdf.
get - запрос данных, endpoint - точка откуда мы получаем данные, body описывает то, что мы хотим получить, в каком объёме и с какими фильтрами.
Если в get нельзя положить текст запроса в body, то для сложных запросов приходится городить запрос post с текстом хотелки и его типом (читаем, изменяем или ещё что), по сути дублируя запросы http и потом на бэке разбирать, а что у нас вообще хотят попросить (graphql привет).
Но ведь запросы http для того и предназначены: get - запрос данных, post - отправка новых данных на сервер, put/patch - изменение данных, delete - удаление данных
Уже готовые запросы, которые много-много лет просто работают, так зачем поверх них вертеть ещё один слой абстракции, или делать костыли в виде длинных и сложных параметров в url?
Если я не прав, то объясните в чём. И не надо ссылаться на стандарт, его тоже пишут люди, и они могут ошибаться. Но если есть объективная причина, то вот её и хотелось-бы прочитать.
Если смотреть юридически:
Можно вычитать из RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1:
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
По определению, данные должны определяться с помощью URI. Посмотрите также первый комментарий от Mnemonik, там подробнее расписано про этот RFC.
Если смотреть философски:
Думаю, что авторы RFC хотели сделать GET методы более легковесными, без Request Body для удобства использования, для облегчения передачи данных, для упрощения кэширования запроса (URL меньше байт займет, чем JSON с теми же параметрами).
Возможно еще для "ошибки от дурака" и более простой поддержки разделения вносить изменения/не вносить изменения: нет Request body - точно идешь по второй ветке, и никакой GET на базу не влияет.
Но это, честно говоря, мои личные домыслы, не судите строго
Если по факту:
Как уже было сказано, в статье и комментариях, Request Body просто отваливается по пути следования GET запроса. И тут против танка не попрешь, как говорится, приходится смириться и принять это за правило.
По моему опыту Request URI покрывает 90% потребностей по передаче параметров. Из основных недостатков - отсутствие типизации и обязательности. Но в остальном, смотрится довольно лаконично. А в более сложных кейсах уже приходится использовать POST.
Либо сначала сохранять POST запросом параметры, получать их айдишку, и по этой айдишке направлять GET запрос - заморочено, но может принести свои бонусы
GET запросы на практике: правила, принципы и примеры