All streams
Search
Write a publication
Pull to refresh

Comments 18

Когда критериев много — используем метод POST и передаём параметры в теле

Какой-то странный REST у нас получается. Запрос - явный GET, но из-за длинных параметров (и невозможности передать параметры в теле) мы должны использовать POST.

Тогда проще использовать POST для всего. И получим RPC (JSON-RPC)

Спасибо за комментарий! Цель статьи обозначить вектор проектирования, конкретная реализация в разных компаниях и командах может отличаться, всё зависит от контекста.

Использование POST для всего подряд усложняет настройки кэширования, ссылки/закладки и другие оптимизации опирающиеся на rest стандарты

Опять притащили коды статусов HTTP как коды ответов на запросы к уровню приложения.

HTTP всегда должен отвечать 2хx, если запрос с точки зрения HTTP выполнен, 4хх - если ошибка со стороны клиента, 5хх - что-то не так на сервере.

А вот в теле ответа быть уже статус ответа на сам REST запрос, причем желательно не просто одним числом, а поподробнее.

А у вас программист, вызывающий сервис, потом голову будет ломать, что же он перепутал: эндпоинт или фильтр запроса и с многоэтажными матами пытаться парсить строки ответов, следующие за HTTP-статусами в заголовке. Не надо смешивать уровни OSI.

Ну да, точно)

А потом после таких "проектировщиков" в ELK приложение 100% отвечает 200 ОК, при том что там 500-х половина. Successfully Failed антипаттерн какой-то.

Ещё и модель OSI прилетаете. Http, на секундочку, уровень "прикладной", то есть как бы как раз того самого приложения.

Работайте кодами ответов HTTP и не слушайте вредных советов и команда SRE скажет вам спасибо за это.

Не успел вовремя ответить, пример с ELK очень в тему, сам с данной проблемой много возился, спасибо!

А можно подробнее что за ELK проблема? Я вот тоже склонен считать, что HTTP коды должны относиться к HTTP только. Если endpoint не найден - 404, если ресурс не найден - 200 + status: error

Имхо в статье много противоречий, исключений и заплаток.

Добрый день!

Имхо в статье много противоречий, исключений и заплаток.

Относительно какой теоретической базы противоречия?

В конце статьи указал основные материалы на которые опирался при написании. Там где были расхождения, приложил ссылки для углубления.

А можно подробнее что за ELK проблема?

Допустим у меня есть базовый HttpClient и у меня стоит задача: отказоустойчивость, логгирование ошибок, метрики. Я как разработчик хочу просто вызвать метод типа PostAsync(url, query, body ...) и получить в ответе модель, которую создал. Чтобы это было просто и удобно обычно создают или используют готовые библиотеки.

Как библиотека поймет что нужно залоггировать/сделать ретрай запрос? В согласованных системах достаточно будет проверки статус кода.

В вашем же случае нужно вычитывать всё тело запроса и искать там заветное слово: "ok": true|false? "success": true|false ? Вот например API slack, где я с этим сталкивался:
https://docs.slack.dev/apis/web-api/#responses
(elk не прикладываю, так как по итогу использовал библиотеку, а не api на прямую)

Когда в теле все же уместно слать 200 и status/status_code?
Когда есть ясная архитектурная особенность, например работа с большим количеством данных => batch методы.
Я в статье рекомендую:

Атомарность: либо всё, либо ничего.

Но если бизнес-сценарий особенный и например в массовых методах нужно выполнить хоть что-то (например принять хотя бы часть валидных данных на индексацию), то уместно отступать от этой рекомендации, но сложность задачи при этом возрастает в разы.

Противоречия не "с теоретической базой", а логические:

  1. GET /resources/:id - не найден = 404, GET /resources/ - не найдено ни одного = 200

  2. PUT /v1/resources/{id} - 404, если не позволено создавать ресурс "со своим идентификатором"? внезапно.

Правила

  • Глагол после {id}POST /v1/resources/{id}/apply-discount.

  • Метод — чаще POST (есть побочный эффект).

  • Исключения: вычислительные методы и счётчики, которые не меняют состояние → GET.

Расчёт стоимости (вычисление, без изменения состояния)

POST /v1/resources/calculate

Риск — лимит длины URL при больших батчах. Лучше так:

POST /v1/resources/batch-get

Но как же "без изменения состояния"? Я понимаю, что мера вынужденная, но "как же принципы"?

Согласно RFC 9110 рекомендуется использовать POST с телом, вместо DELETE.

Даже в RFC противоречия...

Уместный комментарий! Чтобы из статьи получилась шпаргалка, я постарался сократить теорию до фактов. Для углубления прикрепил "полезные ссылки".

  1. GET /resources/:id - не найден = 404, GET /resources/ - не найдено ни одного = 200

  2. PUT /v1/resources/{id} - 404, если не позволено создавать ресурс "со своим идентификатором"? внезапно.


GET /resources/{id} -> 404, когда конкретного ресурса не существует
GET /resources/ -> 200 с пустой коллекцией ([] или { items: [], total: 0 }), когда коллекция существует, но в ней пока ничего нет.

{id} - конкретный ресурс, он либо есть, либо его нет
resources - ресурс-коллекция, она можно сказать существует всегда (если конечно существует родительский ресурс /parent/{parentId}/resources).

Коллекцию мы часто перечисляем (через пагинацию) и будет странно на этапе перехода по страницам внезапно получить 404. Эту тему можно развивать дальше, например в сторону фильтров, поиска.

POST /v1/resources/batch-get

Но как же "без изменения состояния"? Я понимаю, что мера вынужденная, но "как же принципы"?

Это осознанный компромисс и допустимая практика. Да GET - обязан быть безопасным. Но POST тоже может быть таким, если вы его так определили

А потом после таких "проектировщиков" в ELK приложение 100% отвечает 200 ОК, при том что там 500-х половина. Successfully Failed антипаттерн какой-то.

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

В вашем же случае нужно вычитывать всё тело запроса и искать там заветное слово

Это из другого вашего комментария, но я здесь цитирую. Так в чем проблема получить тело и десериализовать его из JSON в объект типа Result, который содержит Success или Fault? Это все равно придется сделать. Ради чего ориентироваться на статус HTTP здесь, чтобы на пару строк выше логирование вставить?

Ещё и модель OSI прилетаете. Http, на секундочку, уровень "прикладной", то есть как бы как раз того самого приложения.

На секундочку, HTTP является прикладным уровнем только для браузеров, да и то в современных реалиях вряд ли уже. Для REST это по сути транспортный протокол, только более высокого уровня.

Работайте кодами ответов HTTP и не слушайте вредных советов и команда SRE скажет вам спасибо за это.

Я вполне, кстати, реальный конкретный пример привел, когда писал, что мне пришлось ломать голову, как обрабатывать 404 ресурса и 404 уровня приложения, когда все было в кучу свалено.

На самом деле вот то, что мы здесь обсуждаем и есть одна из самых больших проблем REST (не как концепции, а как реализации REST over HTTP), которая приводит к жутковатому переплетению транспорта и уровня приложения: когда часть запросов посылается в JSON, а часть (в GET) компонентом URL; вышеуказанная проблема "как возвращать ошибку" и т.д.

Так в чем проблема получить тело и десериализовать его из JSON в объект типа Result, который содержит Success или Fault?

В целом этим и приходится заниматься когда интегрируешься с подобными API которые возвращают 200 и ошибку в теле.
Но чаще всего большинство современных интеграций оперирует статус-кодом, что позволяет работать с ними "из коробки"

Я вполне, кстати, реальный конкретный пример привел, когда писал, что мне пришлось ломать голову, как обрабатывать 404 ресурса и 404 уровня приложения, когда все было в кучу свалено.

Отличный пример в тему статьи! Тоже бывало ломал голову тот ли dns адрес получилось собрать.
Есть практика: "когда сервер намеренно не хочет раскрывать, что ресурс существует", я опираюсь чаще всего на неё, но не пропагандирую)

На самом деле вот то, что мы здесь обсуждаем и есть одна из самых больших проблем REST

Согласен с вами! Тема очень холиварная

И еще вопрос: как в тех же самых логах отличать 404 некорректного пользовательского ввода, которого всегда дофига, и реальную (архиважную!) проблему потери ресурса вследствие слетевшей/изменившейся конфигурации на одной из сторон? Лезть анализировать тело ответа уже на стороне ELK?

HTTP это не просто транспорт, а протокол прикладного уровня, на который опираются разработчики браузеров, CDN, прокси. Да и при обычной интеграции многие инфраструктуры на это завязаны: ретраи, SLO, алерты и тд. Всегда 2хх, остальное в body, как правило, ломает поведение экосистемы веба. Понимаю, что требование к приложениям у нас могут быть разные, но вся эта практика, книги и гайдлайны не на пустом месте появились - на это я и обращаю внимание читателя

А при чем здесь браузеры, CDN, прокси? Первые вообще из схемы исключаем, ну, если только кто-то не захочет просто браузер как инструмент визуализации использовать для запроса к сервису. CDN и прокси все равно по большей части, они на другом уровне работают. Мож, конечно, у них логирование по-другому сработает, если на некорректный (с точки зрения фильтра запроса) ответить 200, а не 404, но не думаю, что это имеет большое значение.

Как вообще книга? Советуешь к прочтению? Я сейчас другую читаю (Арно Лоре. Проектирование веб-API), если читал её, то что думаешь о ней? Я пока на первых главах

Книга интересная, но думаю, что достаточно прочесть хотя бы одну по проектированию API и дальше углубляться в существующие практики. Потому что, когда я начал сверять полученные идеи с собственными подходами и гайдлайнами крупных компаний, то местами нашёл расхождения, но везде прослеживается общий смысл. На этой почве и пришла идея этой статьи, показать что API может быть последовательным и предсказуемым, а конкретная реализация уже может зависеть от местных правил в компании

Просмотр ресурса (есть побочный эффект записи в лог/метрику)

POST /v1/resources/{id}/view

Почему здесь POST?

Понял почему возник вопрос, в статье поправил немного формулировку:
Просмотр ресурса -> Увеличение счетчика просмотра ресурса

POST - потому что подразумевается изменение состояния (увеличение счетчика)

Sign up to leave a comment.

Articles