
Предупреждение: не сомневаюсь, что наверняка кто-то, прочитав данную статью, возжелает закидать меня тапками, но это его право, я не претендую на истину.
Я начал писать эту статью около двух месяцев назад. Писал по мере возможности и уже почти закончил, как буквально на днях было обсуждение именно этой темы с коллегами. По результатам услышанных там вопросов я добавил еще пару абзацев, чтобы постараться ответить на данные вопросы в комплексе. Эта статья лежала в черновиках с декабря 2022г, я совсем про нее забыл.
В наше время каждый слышал про Web-API и у многих это ассоциируется с REST. Однако этот многострадальный REST всегда какой-то не такой, с ним все время что-то не так.
Разработчики часто спорят и упрекают друг друга, что твой REST недостаточно REST-овый. Это стало чем-то вроде культа или даже религии. Я не планирую рассказывать о том, как правильно готовить REST, но весь материал именно вокруг этого многострадального RESTа.
Вспомним, какие основные ограничения имеет REST:
Модель клиент-сервер
Отсутствие состояния (сервер между запросами клиента не хранит ничего промежуточного, другими словами, для выполнения запроса Б, серверу не нужны данные из запроса А)
Кэширование
Единообразие интерфейса
Слои (клиент не знает, взаимодействует он с конечной системой напрямую или на пути есть какие-то другие сервисы)
Стоит сразу отметить, что REST это ни разу не про HTTP (для кого-то может быть сюрпризом).
Что ты такое несешь? Филдинг разрабатывал REST именно для HTTP!
Да, но REST - это архитектурный стиль, который может работать поверх других протоколов. HTTP - лишь наиболее популярная реализация.
Так уж повелось в наше время, что REST, в основном, работает поверх HTTP, поэтому все вышеперечисленные ограничения выполняются как бы сами по себе, за счет тех средств и цепочек, которые обеспечивают функционирование самого HTTP.
Есть множество статей в интернетах, про то, как нужно его (REST) готовить и в статьях всегда всё красиво и понятно, с примерчиками (парочкой). Но эти примерчики часто оторваны от реальности, они элементарные и всегда заточены под одно и то же – под CRUD-операции (на всякий случай: CRUD — это create, read, update, delete). Из этих операций вытекают HTTP-методы: GET, POST, PUT/PATCH, DELETE. Причем, судя по тем реализациям, которые мне приходилось встречать, понимание методов PUT и PATCH разнится. Эти CRUD операции прекрасно укладываются в концепцию для работы с базами данных, со справочниками – создать/удалить пользователя/товар/услугу, получить данные о пользователе и т.д. и именно такие примеры приводятся в статьях. Но когда речь заходит об операциях – запустить/остановить/отменить (подчеркну: не удалить, а отменить, то есть прервать или сторнировать) операцию, тут возникает сложность.
Сложность возникает как минимум по двум причинам:
из-за процедурного мышления (и здесь нет ничего плохого)
из-за попытки притянуть за уши то, что не очень хорошо притягивается (переложить некоторые операции на ресурсы и домены)
Хочется спросить: а нужно ли; оправданно ли?
Ради чего стремиться к призрачному REST, если в какой-то ситуации он не очень-то и подходит? Но на это упорно не хотят обращать внимание. Кто сказал, что API по HTTP – это обязательно REST? Из моих наблюдений вырисовывается примерно такая картина, что, в основном, везде используется RPC (удаленный вызов процедур), который пытаются маскировать под REST, используя какие-то псевдо-домены с недоресурсами и впихивая всюду DELETE, PATCH и вот это вот всё. Что мы получаем в результате таких попыток – только боль. Это и не REST (в том виде, о котором говорил Филдинг) и не RPC, а какая-то ерунда. REST задумывался так, чтобы перенести глаголы из URI в HTTP-методы, а часть параметров из тела в URI. Разумеется, это удобно, когда можно обратиться к одному и тому же URI, но с разными HTTP-методами и результат будет разным. Условно, как если бы мы работали с обычными папками в проводнике. Но работа с файлами это не только создание/чтение/изменение/удаление. Есть ведь еще перемещение, копирование и выполнение (запуск). С последними тремя как раз возникают разногласия при перекладывании их на REST.
Недавно я встретил несколько кричащих примеров гибрида, когда RPC покусал RESTа, они выглядели примерно так:
DELETE /cat/delete-catPATCH /inv/delete-anyDELETE /foo/delete-foo?param1=value1DELETE /res/delete-some/{id}PUT /res/modify-some
В первых двух параметры удаляемого объекта передавались в теле, но и другие не далеко ушли. Кто-то скажет: «Ха! Ну уж я бы точно так не сделал!» Не зарекайтесь.
Что мы видим из примера выше? – классический RPC. JSON-RPC, если хотите (но не тот, который 2.0). Ну и отлично! Что в этом плохого? Мне лишь непонятно желание слепо натягивать ассорти HTTP-методов на RPC. Зачем? Это попытка таким образом сделать REST? Будто бы этот REST определяется именно использованием определенных HTTP-методов. Три полоски — это ведь еще не Адидас, не так ли? И вообще, мне непонятно, почему появилось это странное стремление делать REST и только его? Ну зачем притягивать за уши или натягивать то, что не очень хорошо натягивается? Какая-то повальная мания. А что вообще такое, этот REST? Давайте вновь посмотрим на список ограничений REST (который приведен выше) и зададим другой вопрос – укладывается ли RPC по HTTP в эти ограничения, если сделать первый вызов с использованием другого HTTP-метода, например, POST /cat/delete-cat? Какому из пунктов ограничений это противоречит?
Да, есть семантика HTTP-методов (GET, POST и т.д.), о которых говорится в RFC7231, но без фанатизма и отрыва от реальности. Примеры выше как раз демонстрируют и то, и другое.
Поймите меня правильно, я не собираюсь спорить с концепцией REST-архитектуры. Я лишь призываю использовать предложенное Филдингом там, где это уместно.
Ок, допустим, вы считаете, что характерным признаком REST являются как раз эти ваши GET, POST, PUT, так в педивикии написано и всё такое. А еще кто-то скажет, что типа эти HTTP-методы помогают понять, какая операция выполняется над ресурсом и предсказать другие операции, ну вот и предскажите, что еще может выполнять PATCH /inv/delete-any? И почему вы считаете глагол в URI менее информативным?
Если вы так любите REST, где же тогда так называемая «обнаруживаемость» (discoverability, HATEOAS), про которую в концепции REST тоже говорится – это когда вызов GET /university/123 вернет в теле не только информацию об учреждении, но и ссылки на все доступные нижестоящие ресурсы
/university/123/teachers/university/123/students
Мне такие реализации (с HATEOAS) встречались крайне редко, ~0.5%.
Или вы считаете это необязательно? Это не такая существенная характеристика, ведь куда важнее всякие DELETE и PUT, правда? Вот они-то уж точно про REST! Так сказать, малой кровью, но уже почти RESTful.
Тема холиварная, поэтому, может быть, к этому моменту у кого-то возникла ко мне ненависть и желание заявить, что я вообще ничего не понимаю. Пусть так.
Однако некто Леонард Ричардсон, автор книги «RESTful Web APIs» и ряда других на эту тему утверждает, что пока у вас нет HATEOAS, ваша модель API не является RESTful.
Но вернемся к примеру выше: DELETE /cat/delete-cat
Этот метод явно не подразумевает какого-нибудь GET /cat/delete-cat в классическом представлении, даже если бы «delete-cat» в этом случае было не императивом, а неким именем сущности. Иначе он бы выглядел типа DELETE /cat/delete-cat/{catId}.
Поэтому я бы предложил следующее:
Если конечная точка (endpoint) не подразумевает использования всего набора HTTP-методов (GET, POST и т.д.), тогда не нужно к нему лепить отдельно стоящий метод в вакууме. Делайте в этом случае обычный RPC. Проанализируйте, сколько в вашем существующем или проектируемом API конечных точек, к которым применяется или планируется применять весь REST-овый набор HTTP-методов. Если к конечной точке применяется один или два HTTP-метода, лучше пересмотреть вызовы этих конечных точек в сторону RPC и использования одного HTTP-метода.
И не говорите, что POST это только про создание нового ресурса.
Вот цитата из RFC7231 п.4.3.3.
Метод POST и для чего он нужен:
- Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;
- Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group of articles;
- Creating a new resource that has yet to be identified by the origin server;
- Appending data to a resource's existing representation(s).
Первый пункт как раз подходит под RPC.
В лучших традициях интернетных статеек приведу около REST-овый сценарий, который не про интернет-магазин, а про железо.
Есть у нас обычная электрическая лампочка. Мы будем управлять лампочкой через API. У нее, по сути, только 2 состояния – вкл. и выкл. Сразу подчеркну: лампочка 1шт. Больше не предвидится.
Далее все по канонам: методом GET мы будем получать состояние; методом PUT мы будем менять состояние (записывать в порт пару байт). Пока что все сходится? Можем ли мы здесь применить POST и DELETE? Лампочка – это физический ресурс, мы не можем ее удалить из патрона, мы не можем вкрутить новую, мы не можем добавить еще одну лампочку к существующей. Какой бы вы прикрутили сюда endpoint чтобы получилось REST-фульно, чтобы не стыдно было перед коллегами и чтобы масштабируемость (если понадобится), и всё такое прочее?
Я бы здесь предложил RPC. С использованием GET и POST, а может быть даже только одного POST и для чтения, и для записи, про GET чуть позже.
POST /lamp/123/commands
{
"action": "turn_on"
}
POST /lamp/123/commands
{
"action": "turn_off"
}
Но ситуация меняется, если бы, например, у нас был какой-нибудь Modbus на нескольких RS-485 и с N контроллеров на каждой шине, тогда POST и DELETE можно было бы использовать для добавления новых устройств, их адресов и регистров... но куда? В некое хранилище/БД. Снова все сводится к хранилищу и вот они наши CRUD-операции нарисовались.
И маршрут был бы примерно такой:
Добавляем устройство в контроллерPOST /ports/1/controllers/240/devices
А теперь нужно вкл/выкл лампочку/ports/1/controllers/240/devices/1
Это будет POST, PUT или PATCH? А DELETE здесь уместен? Вопрос риторический.
Однако если посмотреть на это через призму большинства реализаций и переконвертировать, получится нечто подобное:PUT /lamp/enableGET /lamp/info?id=123
Но это же не REST, это RPC! Почему такая реализация? Потому что процедурное мышление. А еще, потому что метод и маршрут появился не на заре времен, а где-то посередине, когда уже было реализовано много RPC. И вот кто-то попытался-таки навести порядок, начав уборку только в одном ящике стола в то время, как весь стол завален хламом. Так зачем же тогда ломать голову и себе и людям с прикручиванием DELETE и PUT? Особенно, когда нет полной структуры типа /ports/1/controllers/240/devices,
даже когда есть что-то подобное /devices/240 это все равно урезанная ресурсно-доменная версия, которая начинается где-то с середины.
Делайте в этом случае обычный RPC используя только POST и всё будет намного проще.
А как же GET?!
С GET не все так однозначно. Запросить HTML страницу или сущность – нормально, но если запросить отчет на электронную почту или какую-то выборку с большим количеством фильтров, здесь возникают нюансы.
Во-первых, при большом количестве параметров трудно анализировать GET-запросы, выискивая глазками в URI нужное значение, особенно, когда там передается кириллица с процентным кодированием (encodeURI), ну вы поняли.
Во-вторых, ограничение длины. Не сталкивались? URI не бесконечный. 2047 символов для IE11 и Edge, тот же Apache по умолчанию может около 8к, а если у вас параметр на входе принимает несколько сотен значений, а если таких параметров несколько…
В-третьих, (это особый случай для API, но все же имеет место) чувствительная информация в URI GET-запросов может:
Сохраняться в логах веб-серверов и прокси
Передаваться через заголовки referrer между страницами
Отображаться в истории браузера
Попадать в аналитические системы
Хотя современные браузеры и поддерживают политики referrer, полагаться на них при проектировании API - рискованно.
И аргумент про удобство вызова из адресной строки браузера (я слышал его не раз) как-то не очень, если в методе есть заголовки.
В общем, резюмируя, могу сказать, что использовать GET целесообразно, когда:
число параметров не более 3-5;
значения параметров короткие – до 30 символов
не предполагается передача нескольких значений для одного параметра
не предполагается передача чувствительной информации
не используются специфические заголовки
нужно кэширование
Коды ошибок
Мнения расходятся, использовать их или нет. Например, JSON-RPC 2.0 вообще предлагает всегда отвечать 200 OK, это как бы намекает, что с транспортом ошибок не было. Но, на мой взгляд, это как-то очень скучно и сухо (в контексте абстрактного Web-API в целом, а не JSON-RPC 2.0). А как же промежуточные системы, мониторинг и всякие шлюзы, которые, не умея анализировать сам запрос, могли бы принять какие-то решения на основе кода ошибки, уж это им под силу. А еще коды удобны, когда в случае ошибки парсинг ответа не предполагается: когда делается проверка типа if result != OK then return 0. Однако увлекаться кодами ошибок тоже не стоит. В свое время кодов придумали много, но, как показывает практика, в основном используется малая их часть. В разговорном языке люди тоже упрощают конструкции и это нормально – слово короче, а смысл тот же. Да, можно вообще одним известным словом изъясниться, склоняя его всячески, но это уже перебор ;)
Что в имени тебе моем?
Еще одна распространенная ситуация, которую хотелось бы подсветить. Когда делаете API, важно помнить один момент, которому, почему-то, не придают большого значения. Если вы создаете интерфейс для внешнего потребителя (команды/системы), который не знаком с особенностями внутренней кухни, не используйте внутреннюю терминологию продукта/компании при именовании методов и/или параметров/атрибутов. Интерфейс должен быть понятен с первого взгляда на запрос и ответ. Когда вы смотрите на любое устройство впервые и видите на нем кнопку с изображением полукруга с вертикальной чертой внутри
( | ) и даже без надписи вкл/выкл, вы уже знаете, что она делает. С API должно быть точно так же. Простой пример – параметр называется MSISDN. Вы уже знаете, что в него передать? Наверняка знаете, если имели отношение к телекому, а если вы всю жизнь проработали в ритейле? Если делаете API для клиента с улицы, то он быстрее поймёт, что от него хотят, когда параметр будет называться «телефон» или «номер телефона» (PhoneNo). Или параметр Party. Думаете, речь про вечеринку? ))) Нет, речь про организацию/компанию. Уверен, многие скажут про корпоративную модель данных… КМД – это хорошо, но вы делаете интерфейс для клиента, а не для себя, поэтому забудьте про свою религию, наступите на горло предубеждениям, даже если они тысячу раз правильные.
Разумеется, если API предполагается использовать только внутри контура или даже внутри продукта, то КМД и общепринятые внутренние понятия наоборот облегчат понимание для разработчика. Но могут быть и такие глубинные вещи, которые знают только создатели конкретного продукта или процесса. Так бывает, что издревле что-то назвали неверно и, как говорят потом: "так исторически сложилось". И вот они (создатели) пытаются отразить глубинный смысл этого процесса в названии API-метода, при том, что сами создатели им (методом) пользоваться не планируют, они его делают для использования внутри контура, но другими командами. Здесь клиенты – это другие команды, они – потребители, для них делалось. Зачем вкладывать глубинный смысл в сложную аббревиатуру или набор слов? Упростите название так, как это выглядит со стороны для потребителей, а не для вас.
Заключение
Труъ RESTful – весьма специфичная штука, которая
Хорошо подходит для CRUD, но плохо для процессинга
GET приводит к трудностям при работе с крупными сущностями, когда у сущности сотни атрибутов, но они нужны не все и не всегда (про GraphQL отдельная история).
Я не предлагаю отказываться от REST, но его нужно использовать только там, где он уместен, где при завершении реализации у вас появится достаточное количество ресурсов с директориями, а не один отдельно стоящий метод (именно метод, а не endpoint) в пустыне. Там, где больше нужны процессинги, лучше использовать RPC (не тот, который JSON-RPC 2.0) по HTTP c системами путей и каталогов в конечных точках, без "Action":"Delete" в теле и без DELETE в HTTP-методе, но с глаголом в URL. То есть от REST нужно взять то лучшее, что в нем есть – система каталогизации, использование силы HTTP (коды ошибок, кэширование), а от RPC взять понятность, с точки зрения процедурного мышления. Двух методов GET и POST достаточно. DELETE, PUT, PATCH используйте только там, где к конечной точке применим весь набор этих методов вместе с GET и POST.
Ссылки по теме, если моих аргументов вам недостаточно:
REST vs JSON-RPC?
чтобы еще больше вас запутать: две антистатьи
Один, Два
Напоследок
Мартин Фаулер: "RESTful purity is not the goal - meeting business needs is."
Рой Филдинг: "REST is software design on the scale of decades: every detail is intended to promote software longevity and independent evolution."
Немного психологии или почему все так произошло с REST
Практика показывает, что людям понятнее/привычнее, когда есть глаголы, потому что большинство самых распространенных языков программирования это императив. Поэтому такая модель подсознательно транслируется и в методы (в их названия). Появление такого количества императивных языков и их популярность именно в том, что это совпадает с психологией человека. Человеку привычнее сказать "иди копать яму" (в бэкэнде сплошной императив), а не "яма должна быть выкопана" (декларатив). Это психология, ничего с этим не поделать. И во всей этой причинно-следственной связи вызывает диссонанс лишь то, что есть HTTP-методы (GET, POST, PUT и т.д.), которые сложно укладываются в голове в контексте команд. Приходится переключать мышление на ресурсный уровень. Нужно думать с позиции домена и как-то это уложить в логическую модель, а мозг говорит - глагол! Надо глагол! Хотя он и есть, этот глагол, но не в имени эндпоинта, а спрятан под капот HTTP. С одной стороны классический REST очень красивый, с другой стороны он слишком противоречивый. Почему все за него цепляются не понятно. Хотя нет - все понятно! Потому что не появился новый Филдинг, который бы презентовал новую хайповую модель. У всех людей всегда проблема с выбором. Людям нужен кто-то, кто скажет как правильно делать. И как бы не рассказывали про то, что люди сами хотят принимать решения - враньё! В глубине души большинство хотят, чтобы кто-то принял решение за них, какой-нибудь Филдинг.
По большому счету, я не сказал ничего нового, многие все это и так делают, но не хотят признаться, что REST, в их исполнении, вовсе не REST и часто больше походит на RPC, но не до конца RPC.
Из всего вышеописанного вытекает понятие REST-RPC.
Это то, к чему все стремятся интуитивно.
Это то, что в большинстве случаев получается у многих при попытке реализовать REST без глубокого погружения в его философию.
Это командный RPC интерфейс поверх HTTP, который использует каталогизацию и статусы ошибок из REST, но глаголы в эндпоинтах из императивных языков и POST, как универсальный метод для передачи.
Это гибрид REST и RPC.
Комментарии (24)

Kerman
11.10.2025 12:36Но, на мой взгляд, это как-то очень скучно и сухо.
Я предпочёл бы скучно, сухо и предсказуемо незабываемому и захватывающему дух приключению по поискам непонятной ошибки. Может старый стал. Теперь люблю, когда скучно.

rt001 Автор
11.10.2025 12:36Ваш личный опыт - самый правильный учитель :)
Я лишь поделился своим опытом и наблюдениями, быть может, они окажутся полезными тем, у кого своего опыта недостаточно.

kozlyuk
11.10.2025 12:36Статья бьет типичным реализациям не в бровь, а в глаз! Однако я не согласен с выводами и частью советов.
Труъ RESTful – весьма специфичная штука, которая хорошо подходит для CRUD, но плохо для процессинга.
Суть REST в том, что если процессинг представить как CRUD, то получаем доступ к возможностям, которые очень полезны для долгоживущих открытых систем в неидеальной сети. Заменив процедурный вызов "обработать" на ресурс "обработка", мы можем естественным образом сделать: просмотр результатов обработки после её завершения (GET) с кэшированием, устойчивый к обрыву связи идемпотентный запуск (PUT), запрос "есть чё?" (HEAD).
Вы предлагаете включать лампочку через POST. Запрос упал по тайм-ауту. Что будет, если отправить его заново — ошибка "уже включена"? успех? А вот клиенту надо логировать успешные включения, автор API предусмотрел в ответе признак, нами ли она включена и когда? REST через HTTP позволяет всё это решить стандартными средствами, достаточно соблюдать семантику глаголов. Даже если не соблюсти сразу, то из стандарта известно, как должно быть (conditional PUT). Реализуем — и не надо рассказывать, как пользоваться этим именно в нашем API.
REST задумывался так, чтобы перенести глаголы из URI в HTTP-методы, а часть параметров из тела в URI. Разумеется, это удобно, когда можно обратиться к одному и тому же URI, но с разными HTTP-методами и результат будет разным.
Вы этими словами противоречите собственному абстрактному, правильному описанию REST из начала статьи. Очевидно, что над бизнес-сущностью могут быть возможны операции, которые глаголами HTTP напрямую не описываются. Проблема в том, что семантика этих операций специфична для API. Клиенты её не знают; хуже того, сам автор мог не всё продумать, как в примере с лампочкой. Задумка REST в том, чтобы свести все операции к набору стандартных. С параметрами происходит следующее: есть "что" — субъект операции, которого мы идентифицируем (I в URI), а есть "как" — именно параметры конкретной операции. Разделение нужно, в основном, чтобы по URI кэшировать.
Насчет психологии процитирую умного человека:
RPC людям нравится не за то, что у него есть преимущества, а за то, что они не сразу видят его недостатки.

rt001 Автор
11.10.2025 12:36Отличное замечание. Спасибо за уточнение философской глубины подхода!
Однако я сознательно упростил формулировку.
Моя цель была показать практическую разницу, которую видит разработчик при переходе от RPC к REST.
Технически вы правы - REST это не просто "перенос", а смена парадигмы.
Но психологически для разработчика это выглядит именно как "перенос глаголов". Большинство разработчиков не вникали в суть философии REST, отсюда и такие реализации. Повторюсь - REST очень красив, но у него высокий порог вхождения из-за его абстракции, иначе не было бы столько обсуждений вокруг него. Ваша ссылка на RSDN это доказывает, но там человек хочет разобраться и грамотный специалист ему объясняет на пальцах. Никто не обсуждает TCP/IP в таких масштабах, потому что протокол, там все понятно. В современном мире, ждать, что когда-то джуны познают эту философию - это что-то из области фантастики. Когда вокруг фреймворки и фреймврками погоняют, когда программируют мышкой, когда никто не знает и не хочет знать, что происходит под капотом метода из библиотеки, которая вызывает другую библиотеку, а та использует третью, мы получаем тонны багованного софта, который никто никогда не будет исправлять и который тратит ресурсы железа. Откуда столько фреймворков? Потому что это попытка (безусловно, успешная) снизить порог вхождения, упростить процесс разработки, повысить скорость разработки. Если человек выбирает фреймворк, он уже практически находится на пути невежества. Лишь единицы понимают, что происходит под капотом какого-нибудь коробочного ORM, а большинство перебирают в цикле строки, будто это какой-то массив в памяти. О какой философии REST может идти речь?Это может быть сколько угодно неправильно, это может сколько угодно противоречить философии, но это факт и этого не изменить. Я лишь поднимаю этот факт на поверхность и даю несколько рекомендаций, как жить в такой ситуации.
Идемпотентность - прекрасное замечание. Но ведь это не свойство самого PUT, это вопрос некой договоренности, что при реализации PUT нужно делать идемпотентность, надо было об этом где-то почитать.
Давайте переложим ваш вопрос с лампочкой на JSON-RPC 2.0. Что будет при повторном запросе? Ошибка? Выключение?
Что мешает сделать идемпотентность при реализации POST? Отсутствие договоренности, из-за которой вы не можете быть уверены, что она есть?
А вы уверены, что те, кто реализовывал такие кривые методы, которые я приводил в статье, знали про идемпотентность и реализовали это свойство для PUT?
Я вот не уверен. Если они не смогли нормально разложить операции по доменам, есть ли гарантия, что для PUT они сделали как надо? Остается только надеяться, что они про это слышали и не забыли.
Идемпотентность должна быть всегда, в случае с POST в REST-RPC, этого требует сам подход, это неизбежность. При реализации такого подхода разработчик сам столкнется с этой проблемой при первых тестовых прогонах и реализует идемпотентность, если он о ней забыл. Ведь других методов нет.

talebin
11.10.2025 12:36Спросите любого архитектора - ему, по большому счету, плевать, какие части HTTP протокола его заставят использовать или не использовать. Его задача - управлять сложностью! Он может что-то любить или не любить, но проблем от HTTP он не почувствует. Не стоит себе льстить: HTTP протокол - это не теория струн, он бесконечно проще того, чем архитектор обычно занимается.
Но вот что архитектора действительно волнует - это границы и межкомпонентное взаимодействие. Именно здесь чаще всего рождается лишняя сложность. И если вы предложите разрушить абстракции и аморфно склеить какие-то слои системы, опытный архитектор точно спросит: “Зачем?”. Ведь он знает, что плата высока, и будет безумием добровольно ее заплатить, не получив ничего взамен.
Архитектору плевать, когда люди обсуждают, чему именно они хотят разрешить протечь из транспортного уровня в прикладной уровень (логический уровень взаимодействия систем). И плевать, какой аббревиатуре это будет соответствовать. Важен только сам факт того, что граница нарушена.
Вот вы, например, пытались-пытались вырулить, по крохам собирали участки, где можно уменьшить сложность до удобоваримой. Дописали до раздела обработки ошибок и…
200 OK, на мой взгляд, это как-то очень скучно и сухо… как же промежуточные системы
… и все - транспортный уровень всеми своими кишками протек в приложение. “Скучно ему, видите ли”. Архитектор полностью перепишет эти два слоя, уберёт границу из кода, блокеры из диздока и повысит смету проекта. Какая теперь разница, что именно протекло - можете тащить и все остальное. Урон - нанесен, код - готов, банкет - оплачен.
И проблема с REST именно в этом - вас миллионы. У кого-то протекли коды ошибок, у кого-то глаголы, у кого-то управление кэшами; кто-то тащит родные возможности по управлению идемпотентностью; кто-то боится не быть готовым к мифическим network middleware; кто-то переносит части тела в URL, чтобы найти их в логах; у кого-то протекла аутентификация; кто-то не может работать, если стандартная панель браузера не парсит трафик; кто-то тащит все, для чего есть подсветка синтаксиса в .http файлах; кто-то привык к популярному фреймворку; кто-то не знает свой CDN и готовится ко всему на свете...
И нет никакого смысла писать статьи о том, кто из них более неправ либо менее неправ. Или о том, как нужно изменить аббревиатуру, чтобы было не стыдно.
Как поэтично говорит один из моих знакомых архитекторов: “Если оборона пала, то неважно, что я буду писать в диздок - все равно весь хлеб будет украден, а все девки изнасилованы - это у варваров в крови”.
Спросите, что же делать? Можно ли вообще использовать REST или что-то на него похожее? Конечно, но! Только если это является реальным требованием в техническом задании проекта, с обоснованием в виде твердого намерения использовать те свойства и возможности, которые он предоставляет, и с финансированием последствий. А не мифическое: “а вдруг через 5 лет мы захотим мигать лампочкой в коридоре при неправильном пароле - без изменений в коде”.
Намек начинающим: не переживайте, если вам кажется, что с REST что-то не то, но все вокруг ведут себя как слепые - вы не одиноки, с вами все в порядке. Просто некоторые темы ковырять не принято, чтобы экономить ресурсы, раз уж непоправимый урон уже нанесен. Работайте с тем, что есть, пока не дорастете до уровня принятия решений.

gmtd
11.10.2025 12:36Когда-то написал пару статей про JSON-RPC (https://habr.com/ru/articles/709362/, https://habr.com/ru/articles/710652/)
Не понимаю людей, знающих о JSON-RPC, но продолжающих использовать REST и жаловаться на него. REST неизлечим, там проблемы в идеологии и архитектуре
Весь web 3.0 на JSON-RPC не потому что это хайпово (как раз-таки наоборот), а потому что это удобно

talebin
11.10.2025 12:36Полностью поддерживаю. Несколько раз сталкивались с JSON-RPC (работали со stratum протоколом и межсервисным велосипедом), остались только самые положительные впечатления.
Кстати, такое забавное наблюдение: JSON-RPC реализованный слабым разработчиком, намного менее проблемный и более приятный в работе, чем RESTful протокол, реализованный моим лучшим разработчиком.
И побочный эффект: если какая-то команда попробовала оба варианта - потом всегда будет напоминать и просить JSON-RPC, даже если ТЗ предполагает что-то другое.

wa7ker
11.10.2025 12:36REST неизлечим, там проблемы в идеологии и архитектуре
Было очень интересно почитать коменты к этой статье, как и саму её.
Попал во всё это почти случайно - во время разработки REST API для web-app.
Опыта конкретно в этой теме довольно мало, поэтому очень интересно
почитать тех, кому уже есть что сказать в силу именно практически
накопленного опыта.
Сам, разумеется, эту тему ещё погуглю, но может у вас с ходу есть ссылки или компактные тезисы, доказательства этих самых неизлечимостей, проблем в идеологии и архитектуре RESTa?
Спасибо!
Rsa97
11.10.2025 12:36То, что с ходу:
REST завязан на транспортный протокол "запрос-ответ", например HTTP (REST вообще сильно прибит к HTTP). На асинхронных протоколах, например websocket он не работает.
Возврат ошибок в REST завязан на коды ошибок транспорта. В результате может быть сложно отличить ошибку транспорта от ошибки приложения. И ошибок приложения может быть гораздо больше, чем стандартных кодов ошибок в протоколе.
REST хорошо ложится на CRUD (Create, Read, Edit, Delete <=> POST, GET, PUT/PATH, DELETE), когда мы работаем с наборами связанных сущностей. Но для многих вещей приходится вводить искусственные сущности. Например, в REST нельзя послать команду "включить лампочку". Мы либо меняем свойство "включено" сущности "лампочка", либо, в более сложных случаях, когда требуется набор связанных действий над несколькими сущностями, вводим новую псевдосущность "включение лампочки" и запрашиваем создание новой такой сущности.

koleso_O
11.10.2025 12:361) Нет такого протокола, как «запрос-ответ», rest никак не завязан на такой модели взаимодействия и при желании его можно положить на вебсокеты (просто так не принято); rest никак не связан с http, его можно реализовать и на tcp (некоторые технологии rpc умеют в это), просто на http проще реализовать, удобнее дебажить
2) согласен
3) Нет ничего страшного в добавлении новых сущностей в вашу предметную область, так как, напомню, абсолютно все ваши сущности являются искусственными

Rsa97
11.10.2025 12:36И как положить REST на websocket или другой асинхронный протокол? Примеры есть?

rt001 Автор
11.10.2025 12:36Обычно колбэки в этом случае применяют, если по HTTP
Что-то типа
POST /process/123/commands{"action": "exec""replyTo": "https://foo.any/api/foo"}Можно применить REST при работе с брокером сообщений. Очереди/топики, как ресурсы, например.
Для вебсокета
Сообщение{"method": "POST", "resource": "/process/123/commands"}Ответ
{"status": 200, "body": }
Rsa97
11.10.2025 12:36У websocket нет понятия "ответ". Есть "сообщение".
Клиент посылает сообщения серверу и получает сообщения от сервера. Эти сообщения никак не связаны друг с другом и порядок их прихода не регламентирован.
То есть, когда клиент получил сообщение от сервера, он не может сказать, в ответ на какое из его сообщений оно пришло, и вообще, ответ это или другое сообщение от сервера, например ping.
Вам придётся добавлять в сообщение идентификатор запроса, чтобы ответ пришёл с тем же идентификатором, добавлять какой-то механизм привязки каллбэка/промиса к этому идентификатору и, фактически, вы получите тот же JSON-RPC, только с ограниченным набором методов.
rt001 Автор
11.10.2025 12:36Вы спросили как, я ответил
Но я не говорил, что это просто :)Вы правы, что это куча накладных расходов, но, при желании, этот стиль можно натянуть не только на HTTP.
По поводу ответа для сокета, я лишь написал это слово (Ответ) чтобы две строчки отличались по смыслу, чтобы было понятно направление сообщения, а вы зачем-то меня тыкаете носом в техническую неточность. Я ведь не на экзамене и у нас нет задачи друг друга на чем-то подлавливать в этой дискуссии... Или есть?

Rsa97
11.10.2025 12:36Я не тыкаю вас носом, а просто показываю разницу между синхронным HTTP и асинхронным websocket.
Да, что ещё интересно, если посмотреть исходное предложение по REST самого Филдинга, то у него нигде не упоминается, что действия ограничены типами HTTP-запросов (GET/POST/PUT/DELETE). Он вводит ограничение только на то, что все действия происходят над ресурсами. То есть, запрос JSON-RPC{ "jsonrpc":"2.0", "id":1, "method":"turnOn", "params":{"resource": "/lamp/kitchen1","jwt":"abc...890"} }Тоже вполне будет REST-запросом по Филдингу. Так что согласен, REST по асинхронному протоколу возможен, только это будет не то, что, как правило, понимается под REST.

lexus1990
11.10.2025 12:36В глубине души большинство хотят, чтобы кто-то принял решение за них
Согласен. Решите за меня пожалуйста

VanKrock
11.10.2025 12:36Я для себя использую гибрид так как обычно пишу бэк для Single Page Application
У меня все запросы POST (кроме аутентификации, там GET по стандарту)
Ошибки делятся на инфраструктурные и ошибки бизнес логики (инфраструктурные - это те которые можно описать на уровне middleware для всех запросов например 401, 403) такие ошибки обрабатываются на фронте так же на уровне всего приложения
В отличии от JsonRpc статус success или error пишу в хедере, чтобы тело запроса можно было сразу десериллизовать в нужный тип, traceId так же в хедере, нет params так как не требуются batch запросы, зато можно результат возвращать стримом по мере поступления данных и сваггер выглядит нормально, да и в Google Chrome Developer путь запроса нормально отображается и не приходится гадать, а какой там метод вызывался с фронта
pilot114
вы пару раз критически упоминули json-rpc 2.0, но не раскрыли в чём его минусы.
Почему там всегда 200 понятно - изза батчинга. А что ещё?
supercat1337
Один из недостатков: json-rpc в спецификации не гарантирует порядок результатов при пакетных запросах. Но я бы не сказал, что это фатально. Если разработчику важен порядок в массиве результатов, то он просто учтет этот момент у себя в коде. То, что нет четко закреплённого списка ошибок - тоже спорный момент.
Статус 200 - это просто успешный транспорт payload. И дело тут не в батчинге.
Rsa97
Вместо фиксации порядка в JSON-RPC 2.0 используется id запроса, который возвращается в ответе.
Некоторый фиксированный набор ошибок там тоже есть, он связан с ошибками в самом протоколе. Ошибки приложения разработчик определяет сам.
rt001 Автор
Я вовсе не критиковал json-rpc 2.0, а лишь приводил его, как противовес. Но не рассматривал его как альтернативу, потому что и в нем всё не так, как разработчикам хотелось бы интуитивно. Он не плохой, со своими задачами справляется, но, как сказал Билли, в исполнении Караченцова, в фильме Человек с бульвара капуцинов:
Каждый раз в статье, упоминая про json-rpc 2.0, я лишь подчеркиваю, что большинство реализаций псевдо-REST это по факту какой-то JSON-RPC или HTTP-RPC или как-то иначе его назвать, но он ничего не имеет общего с json-rpc 2.0, вот какая мысль звучит в статье. Название json-rpc уже занято.