Комментарии 207
Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.
Поправьте меня если я ошибаюсь, но суть CSRF не в воровстве куков. Если мы можем украсть куки, то нам не страшны никакие CSRF токены, потому что мы просто будем использовать куки, а токен нам пришлет сервер.
CSRF токен при грамотной реализации позволяет защититься от атак типа вставки картинки в письмо. Но как его грамотно реализовать? Если мы лепим его в куку или в параметр (т.е. в url для get), то токен должен протухать при каждом новом запросе, что не очень удобно, когда клиент запускает запросы асинхронно целыми пачками. Передача дополнительного заголовка позволяет продлить жизнь токенам, плюс требует обязательного использования javascript или иного кода клиентом. И при этом может спасти, даже если клиенту подсунули трояна с curl, имеющего возможность прочитать живую куку прямо с диска.
Я говорил не про важность использования CSRF в целом, а про конкретную цитату
куки можно украсть, в чём собственно и состоит суть CSRF атаки.
Для эксплуатации CSRF нет необходимости красть куки.
CSRF токен при грамотной реализации позволяет защититься от атак типа вставки картинки в письмо.
То есть атаку на GET? Если GET не меняет состояние, то CSRF для него не нужен.
И при этом может спасти, даже если клиенту подсунули трояна с curl, имеющего возможность прочитать живую куку прямо с диска.
Но как? У нас есть кука, которую достали трояном. Мы с этой кукой идем на сайт и все, никакой CSRF токен нам больше не нужен.
То есть атаку на GET? Если GET не меняет состояние, то CSRF для него не нужен.
Нужен, если передаются конфиденциальные данные, которые мы не хотим отдавать кому попало.
Но как? У нас есть кука, которую достали трояном. Мы с этой кукой идем на сайт и все, никакой CSRF токен нам больше не нужен.
Идёте на сайт со свежеворованной кукой и на любой, в том числе get запрос получаете 419 Authentication Timeout, потому как CSRF токена у вас нет. Для продолжения работы вам нужно ввести пароль, который трояну украсть негде.
Украсть живой токен конечно тоже можно, но уже значительно более сложным трояном (со сниффером и подделкой https сертификатов или чтением памяти браузера).
Ок, принято.
Нужен, если передаются конфиденциальные данные, которые мы не хотим отдавать кому попало.
А каким образом "кто попало" их получит? Рассмотрим ту же картинку с запросом, пришедшую на почту. Для пользователя она будет просто незагрузившейся картинкой, а злоумышленник её вовсе нигде не увидит.
Спертой куки хватит для авторизации, но не всегда есть возможность ее прочитать, иногда можно только вслепую ей воспользоваться (та самая картинка). И вот тут уже приходит CSRF токен в месте c Referer, которые тупо не дают сходу выполнить любой значимый запрос, кроме инициализации новой сессии (предъявляем куки) с получением CSRF токена. Как-то так.
Добавьте, пожалуйста, в ваш список: Никогда не передавайте конфиденциальные данные методом GET.
Далее, использование кукис при реализации CSRF Protection — корпоративный стандарт для многих фрэймворков и CMS.
Также, как уже вам заметили другие комментаторы, транспортный слой накладывает свои ошибки, поэтому например «404 эндпоинт на целевом application-сервере не поднят» вы никак не отличите от «404 эндпоинт на целевом application-сервере поднят, но ресурс на нём не найден». Ниже пользователь rpiontik спрашивает, какая разница между этими случаями: разница здесь в совершенно разных сценариях обработки этих ошибок.
Язык С++ как-то обходится 7 типами исключений. В ОС UNIX за 50 лет развития придумали чуть больше 100 кодов ошибок, из которых 2/3 никогда в реальной жизни не используется. Я, конечно, видел иерархии исключений на 1000+ пунктов, только это людям в больших конторах заняться нечем, а оправдывать свою нужность и незаменимость хочется.
В HTTP из коробки 200 кодов (100 4хх и 100 5хх), которые можно использовать. Вот и используйте. Речь именно о цифровых кодах; для конкретного сообщения у вас есть текст ошибки.
Изобретатели велосипедов типа вас и GreenLordUA предлагают, получив в подарок автомобиль, выбить лобовое стекло и запрячь лошадьми. Потому что это ж блин надо учиться водить машину.
Ну если вам не нужен протокол HTTP и вся инфраструктура, которую он предлагает (браузеры, либы, веб-серверы, прокси, балансировщики, PKI наконец) — ну напишите свой бинарный протокол поверх UDP на левом порту, и обрабатывайте ошибки как вам нравится. Делов то. :)
В HTTP из коробки 200 кодов (100 4хх и 100 5хх), которые можно использовать
Неправда, из коробки в стандартном HTTP кодов почти на порядок значительно меньше.
Да, вполне может быть, что вы не сталкивались со сложными бизнес-процессами и Large Enterprise. В SME может быть, да и то мало где. В микросервисной архитектуре на обработке бизнес-ошибок строится огромный кусок оркестрации: перезапустить контейнер, если 404 или пересоздать отсутствующий ресурс, если отдать специальный код а в теле уточнить, что resource not found (это частный микропример, а можно и запустить процесс создания кучи связанных ресурсов, если они связаны в модели и много чего ещё)
Кодов из коробки совсем мало, нет там и близко двухсот, а те что есть никак не маппятся на бизнес-логику, потому что имеют свое назначение и описание.
Как в таком случае отличить 404 Resource not found слоя ппидожения и слоя транспорта. Бизнес требует в первом случае показывать сообщение пользователю чтл ресурс не найден, во втором случае показывать сообщение Что-то пошло не так. Попробуйте позже
Вовсе не обязательно осмысленный. Представьте ситуацию: есть сайт http://examle.com и его api http://example.com/api. Содержимое сайта отдаётся каким-нибудь nginx, а на обращения к api отвечает бакэнд, но запрос всё равно идёт через nginx.
Теперь если админ ошибётся в настройке nginx — то в течении какого-то времени на любой запрос к api будет выдаваться 404!
Переходим к ситуации номер два. У нас Windows, nginx заменяем на IIS, а запросы к api пусть обрабатывает служба напрямую через HTTP.SYS
Теперь даже в настройке IIS ошибаться не требуется — достаточно перезапустить службу, и пока она перезапускается на запросы к api будет отвечать IIS. Кодом 404.
И да, усложняя пример можно получить в ответ на запрос любой код HTTP кроме 418...
Это все тот же осмысленный ответ — нет обьекта по этому uri. Не понимаю проблемы. Ну нет там его. Какая разница почему?
GET /api/banned-users/15/
И на основании ответа решает разрешить ли пользователю логин или нет. Если опираться только на статус 404, то в любая непредвиденная ситуация (сломался роутинг, опечатились в адресе апи в коде, эту апи вообще удалили) будет приводить к серьезным багам в бизнес-логике.
А то, что вы пытаетесь донести — о кривезне рук какого-то там админа, так для этого есть интеграционные тесты. И не нужно прикладным уровнем затыкать амбразуру их остуствия и предполагаемую безответсвенность админов.
Используя REST вы принимаете решение о работе на уровне HTTP протокола. И нужно принимать его таким, какой он есть. Если вы начнете его под себя гнуть, в итоге он нагнет вас. Потому, что это за рамками вашего проекта.
Хотите консистентность — RPC. Но автоматом вы теряете все профиты HTTP.
habr.com/ru/post/440900
Это плохая бизнес-логика, если она может на этом строиться.
Что значит плохая бизнес-логика? Что вы будете делать, если в бизнес-логике предусмотренны разные сценарии в зависимости от того существует объект или нет?
К каким серьезным багам будет приводить? Поясните.
Я же специально выше привел конкретный пример. В данном случае произойдет неправильное определение пермишена пользователя, т.е. сможет залогиниться тот, кто на это не имеет право.
Да и вообще примеров можно много привести. Любой кейс когда от существования объекта зависит логика. Например, могут удаляться связанные с этим объектом сущности
Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите. Вообще. Вам кто угодно может ответить что угодно. Это суть HTTP. Это куча серверов между вашим фронтом и бэком. Админ ли у вас глупый или еще кто-то, какой-то вы там код свой придумаете или другой используете это ничего не поменяет. Только усложнит понимание вашей системы теми, кто будет в ней разбираться. И все.
Повторюсь еще раз, хотите консистентность это — RPC. Но вы автоматом теряете все профиты от HTTP.
Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите. Вообще.
Ну так задача-то была контролировать. Если контроля нет — значит, задача не решена.
Ну не сможет. И что? Ответьте на вопрос — и что?
Вообще-то как раз сможет, хотя не должен. Кейсы могут быть разные от не критичных до очень критичных, которые могут выливаться в реальные финансовые/репутационные потери для бизнеса. Странно, что вы этого не понимаете.
Проблема то где?
Собственно в потенциальных багах, которые были бы невозможны в случае если 404 не будет являться ожидаемым респонсом.
Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите.
Я не понял как это связано с обсуждаемое темой. Мы обсуждаем два варианта:
1. Когда мы можем отличить два состояния «ошибка роутинга» и «объект не существует»
2. Когда мы не можем отличить эти два состояния
2-ой вариант может приводить к багам, пример которых я привел, причем такие баги не всегда просто будет обнаружить. В 1-ом варианте эти баги исключены. К тому же если 404 не считается ожидаемым респонсом, то появление респонсов с таким статусом в логах приводит в срабатыванию алертов и более быстрой реакции на то, что, например, какой-то endpoint исчез
1. Когда мы можем отличить два состояния «ошибка роутинга» и «объект не существует
Вам не нужно отличать эти две ошибки. В этом нет смысла. В любом случае вы не получите объект. Если уж сильно, сильно, сильно хочится, в респонсе 404 отдавать — Точно, объекта нет. Отвечаю.
2-ой вариант может приводить к багам, пример которых я привел,
Баги это или нет, но они нарушают работу системы как любой другой. И все, что вы можете сделать — зафиксировать наличие баги. Диагностика это вопрос не пользователя и не софта, а специалистов. И для этого есть логи.
В 1-ом варианте эти баги исключены.
Как? Баг есть. Админ роут перепутал. У вас 404. Что делать будете?
К тому же если 404 не считается ожидаемым респонсом
Вообще не учитывается админами. Это крайне распространенный штатный ответ. Разве, что наложены правила на алерты по 404.
какой-то endpoint исчез
Об этом должны говорить интеграционные тесты. Регулярные. Мониторинг. Но точно не уровень прилоежния. Все, что приложение может сказать пользователю — упс… у нас проблема. Обратитесь в сапорт.
Вам не нужно отличать эти две ошибки.
Нужно. В одном случае сервис спокойно упадет с 500кой (ну или как-то обработает ошибку, зная что это нештатная ситуация), а в другом будет выполнена бизнес-логика для случая когда объекта не существует.
Как? Баг есть. Админ роут перепутал. У вас 404. Что делать будете?
500тить, например. Это частичная неработоспособность системы. Плюс к этому на 500ки последует очень быстрая реакция. А в том что предлагаете вы из-за инфраструктурной проблемы каскадно начнет неправильно вести себя бизнес-логика. Получив 500ку я сразу по логам могу установить, что ее причиной было 404 от другого сервиса. А в вашем кейсе сломается логика, но никаких признаков что что-то идет не так не будет
Вообще не учитывается админами. Это крайне распространенный штатный ответ. Разве, что наложены правила на алерты по 404.
Там где 404 не используют как нормальный респонс, конечно, накладывают такие правила для алертинга
Об этом должны говорить интеграционные тесты. Регулярные. Мониторинг.
Роут может сломаться в любой момент, уже после прогона интеграционных тестов. А вот с мониторингом опять та же самая проблема, что мы не знаем в каких кейсах 404 это ожидаемое поведение, а в каких-нет.
500тить, например. Это частичная неработоспособность системы.
Это как? Роут перепутан… ничего до бэка не дошло. Просто 404. Что в этом случае?
Там где 404 не используют как нормальный респонс, конечно, накладывают такие правила для алертинга
Не в коем разе. Это плохая практика. Ибо вашу поддержку могут просто затроллить. Она сума сойдет разгребая запросы.
Роут может сломаться в любой момент, уже после прогона интеграционных тестов.
Может. Но это не значит, что он будет сломан постянно. Через установленный промежуток времени, мониторинг покажет ошибку. Она будет исправлена. Пользователи продолжат работу.
404 мониторингами не отрабатывается. Только для конкретных роутов, где скажем, на любой запрос, должен даваться ответ. Да думаю и там не будет. Обрабатываются 5хх ошибки.
Вообще 4хх не должно обрабатывать мониторингом. Только, повторюсь, в виде исключений.
Это как? Роут перепутан… ничего до бэка не дошло. Просто 404. Что в этом случае?
500тить будет клиент этого endpoint'а, получивший 404. Это нештатная ситуация, в которой клиент не может продолжить работать не получив эти данные.
Не в коем разе. Это плохая практика. Ибо вашу поддержку могут просто затроллить. Она сума сойдет разгребая запросы.
Вы сейчас имеете в виду запросы, которые приходят снаружи (браузер и т.д.)? Я же имел в виду в первую очередь взаимодействие между внутренними сервисами, где никто никого троллить не станет
Может. Но это не значит, что он будет сломан постянно. Через установленный промежуток времени, мониторинг покажет ошибку.
В таком случае мониторинг будет носить вероятностный характер. Т.е. нужны еще какие-то сложные правила, что от определенного endpoint'а должен быть какой-то процент не-404 ответов
500тить будет клиент этого endpoint'а, получивший 404.
Это я вообще не понял… какой клиент endpoint? Но даже если это откинуть, то вы просто преборазуете 404 в 500. Зачем-то… хотя суть ошибок разная. И главное, приравнивает (sic!) 404 к 500. Что мешает это сразу воспринимать как то, что вы хотите?
Я же имел в виду в первую очередь взаимодействие между внутренними сервисами, где никто никого троллить не станет
Хух… да я бы про это и слова не сказал. Тут можно делать что хочешь. Это контролируемый периметр. И, я это не считаю полноценным REST. По субъективным причинам.
В таком случае мониторинг будет носить вероятностный характер. Т.е. нужны еще какие-то сложные правила, что от определенного endpoint'а должен быть какой-то процент не-404 ответов
Да не нужно :) Просто не нужно это контролить. НО!!! Если речь идет о внутренних сервисах и клиентах, то да, 404 вполне резонно стоит контролировать. Тогда в этом есть смысл.
Это я вообще не понял… какой клиент endpoint?
en.wikipedia.org/wiki/Web_API#Endpoints
Если речь идет о внутренних сервисах и клиентах, то да, 404 вполне резонно стоит контролировать. Тогда в этом есть смысл.
Наконец-то мы поняли друг друга
то вы просто преборазуете 404 в 500. Зачем-то… хотя суть ошибок разная. И главное, приравнивает (sic!) 404 к 500
Ну как раз приравнивание ошибок происходит в вашем случае. Недоступность какого-то апи приравнивается к нормальному ответу. Это равносильно приравниванию пустого ответа от субд к отсутствию коннекта к этой субд
Когда будете создавать внешний сервис, когда дойдете до того, о чем я вам пишу, плз, если не сложно черканите, если я окажусь не прав ;)
Это аргумент в стиле вы тут все неопытные юнцы, один я знаю как правильно делать? Или мне показалось?
Один из моих проектов в профиле.
я могу быть уверенным в том, что я говорю
уверенность никак не говорит о вашей правоте или неправоте. У кого-то может уверенность в противоположных вещах.
Один из моих проектов в профиле.
К слову, у вас в профиле никаких проектов нет. По крайней мере, для меня они не видны
www.cryptonit.net в профиле. Надеюсь, я ничего не нарушаю.
Все логика на существование элемента может только подсказывать валидации
Можете развернуть мысль. Я не очень понял что тут имеется в виду.
ибо при создании последнее слово все равно скажет база, смогла ли она создать или нет.
При чем тут создание, если речь шла о получении объекта? И при чем здесь база?
GET /api/banned-users/15/
И на основании ответа решает разрешить ли пользователю логин или нет
Не очень удачный пример. В этом случае надо вернуть пустую коллекцию или null, а не опираться на 404. Но вообще то говоря, рестрикшены не строятся по принципу разрешено все, что не запрещено, когда неизвестно, что именно запрещено.
Не очень удачный пример. В этом случае надо вернуть пустую коллекцию или null
Ну так я как раз об этом и писал, что в этом кейсе опираться только на статус 404 довольно опасно.
Что вы понимаете под "татикой на ваш REST API"? Вот есть сервер приложений, он отвечает на запросы к API. Откуда возьмётся статика?
Дело в том, что вы ограничиваетесь простейшими кейсами. И находите в них проблемы. Хотя их нет. Возьмите и помониторьте серьезные сайты на предмет работы REST. Сделайте выводы оттуда, а не из дебатов на хабре.
А если сделать абы как, то, собственно, и всплывут те самые проблемы о которых и писали выше, когда ответ сервера ничего не означает, а ошибка бизнес-логики неотличима от транспортной.
И делать выводы о том, что чего-то нет в БД неверно. Нужно именно судить о том, что по этому урлу нет объекта. А где его нет… в кэше, в БД или еще где-то это вопрос вторичный. Вы получите 404. И вместо него просто не нужно ничего придумывать. Достаточно это принять и с этим работать.
Если хотите удостовериться в том, что ответ от бэка, то вводится новая сущность в загловки — fingerprint. По которой можно заключить о том, кто эмитировал этот ответ. Но чаще всего, это вредно отдавать фронту. И эта фича используется во внутренней инфраструктуре для идентификации в логах.
REST не БД, даже если ответ берется из БД
Да собственно не важно БД или не БД. У нас есть ресурс с определенным URI и мы хотим проверить существование этого ресурса. Если мы получаем ложноотрицательный ответ (в случае когда 404 вернулся из-за недоступности сервиса) о существовании этого ресурса, то это неконсистентное состояние системы. В каких-то случаях такая неконсистентность допустима, в каких-то — нет
в случае когда 404 вернулся из-за недоступности сервиса
Недоступность сервиса — 503.
А если он проксирует, то другой сервер живет по той же системе. И тоже отдаст 5хх.
А уж если вы запроксируете напрямую в какой-то самопальный бэк… то… отгадайте, что ответит nginx если по этому порту никто не ответит?
Ну почему, я должен вам доносить очевидные вещи? Я уже и так вам «намекал», и так… теперь мне вменят переход на личсноти и т.п. :)))
Ну не может быть такого примера, понимаете?
навскидку:
- Service Discovery стал вести не в тот контейнер
- Ошибка конфигурации API Gateway
- В коде сервиса сделали ошибку, что привело к изменению (или вообще удалению) url'а, и соответсвенно запрос на него всегда возвращает 404
- Ошибка в роутах с regexp'ами типа
user/[0-9]{2})/
user/42/ работает нормально user/666/ всегда возвращает 404
Т.е. с nginx тема не прокатила
Я вообще ничего про nginx не писал.
возмите плз и сами найдите ответы почему эти сценарии надуманы
Понятно. Все ваши аргументы против приведенных доводов сводятся к безапелляционным утверждениям в стиле
Ваш это не нужнои
такого не бывает.
Во-вторых, в том же nginx можно попросту забыть прописать location к api, или опечататься и сделать location для какого-нибудь apo. Если при этом для всего сайта целиком указана директива root — то результатом как раз и станет 404 в ответ на любой запрос к api.
Недоступность сервиса — 503.
Ну не надо передергивать. Вы прекрасно поняли что я имел в виду: например, сломали роутинг. По существу можете что-нибудь ответить? Что делать с неконсистентностью в случае когда она неприемлема?
Прочитайте пожалуйста мою статью habr.com/ru/post/440900 в части «Коды ответа http в REST» там я популярно изложил все стадии «принятие» REST.
Прочитал. 404 упоминается ровно один раз:
Т.е. если не найден объект по запросу, это 404
Утверждение без какой-либо аргументации.
Если говорить про статью, я согласен, например, с доводами про использование http 500 и что вообще хорошо бы уже по статусу отличать состояние, когда все прошло успешно (2xx) от неуспешного (>=400, что с этим дальше делать клиенту уже другой вопрос). Но у меня есть претензия конкретно к использованию 404.
Дабы прервать этот срач, предлагаю со стороны приложения добавлять какой-то заголовок по типу X-Application: true. Тогда можно проверить что ответ именно от приложения. Если только всякие прокси не урежут заголовок
А еще лучше какой-то хеш подпись, тогда можно отфильтровать еще и подделки
Если хотите удостовериться в том, что ответ от бэка, то вводится новая сущность в загловки — fingerprint. По которой можно заключить о том, кто эмитировал этот ответ. Но чаще всего, это вредно отдавать фронту. И эта фича используется во внутренней инфраструктуре для идентификации в логах.
И тут бац
Дабы прервать этот срач, предлагаю со стороны приложения добавлять какой-то заголовок по типу X-Application: true.
потом бац
А еще лучше какой-то хеш подпись, тогда можно отфильтровать еще и подделки
Вопрос… с очевидным ответом — смысл тут распинаться?
А если админ ошибётся, и вы будете получать сутки 204 ответ?
Что за чушь вы несёте?! Пойти и сломать ваш сервер так, чтобы он возвращал чепуху — ерунда для админа. Но есть, короче, метод. На крутых REST проектах админам резиночкой мудики перетягивают, и они перестают генерировать нестандартные ошибки.
З.Ы. если админ ошибётся, вы будете, скорее-всего, получать 502, 503 или Timeout Error (забыл его код). Если ваш админ так не умеет — срочно бегите за резиночкой!
404 — это отсутствие ресурса. А ещё есть целый пласт 5xx ошибок и 4xx состояний.
- Not found objects on search — это 200 код сервера. /api/search?text=substr должно вернуть пустой список при отсутствии результатов и 404 при недоступности /api/search
- /api/path/video/1234 — это конкретный запрос ресурса — ролика с идентификатором. Если отсутствует любой узел, включая 1234, возвращается 404.
И не забывайте, что есть Timeout error, например, Bad Gateway, Service Unavailable. Почему вы думаете, что при проблеме доступа к эндпоинту обязательно будет возвращаться 404 код? 404 код возвращает роутер, проверяющий существование ресурса.
/api/path/video/1234 — это конкретный запрос ресурса — ролика с идентификатором. Если отсутствует любой узел, включая 1234, возвращается 404.Как вы отличите ситуацию «видео с таким номером нет» и ситуацию «у нас еще приложение не до конца задеплоилось, так что роутинг не работает и веб-сервер отвечает стандартным 404»?
Такое api называется JSON-RPC. Не вижу проблем сделать к нему клиента, если не забывать смотреть на тело ответа (а если забывать — не понимаю зачем вообще нужен клиент и api).
Абсолютно реальный, не надуманный кейс, с которым лично я сталкивался три раза: два раза на мобильных клиентах германии, находящихся в роуминге (один из них Т-мобайл, второй уже не помню), один раз — на итальнском мобильном операторе (вне роуминга, в родной сети).
Выглядит следующим образом — все, что попадает на мобильный телефон — проходит через прокси мобильного оператора. Прокси оператора, видя стандартные коды ошибок (400, 401, 403, 404 (!), 500) режет ответ вашего сервера. Он отрезает весь ответ после заголовков (с целью экономии трафика?). Поэтому любые детали об ошибке, которые вы передаете в теле ответа — просто теряются.
А вот 200й ответ не режется никогда (конечно, субьективное мнение, не более). Пережимается (удаляются переносы строк, не значащие символы) — видел. Но это не мешает его правильно интерпретировать клиенту.
Поэтому в случае, если вам нужно быть максимально совместимым — я бы как раз рекомендовал обратное — отдавать ошибки столь ненавистным вам способом.
а при этом решение с ответами 200й будет работать — в любых условиях. https/https, сниферы/прокси, реальные сертификаты/подменные.
просто будет работать. всегда и везде.
или же — кешировать ваши правильные данные (без ошибок) для вашего варианта — вне зависимсоти от того, как вы кодируете на уровне протокола сами ошибки.
плюс — приведу ваш же довод — ssl.
ну а в варианте — прокси, с подменой сертификатов. да и не правильно настроенным кешированием — апи просто не будут корректно работать.
Повлиять на политику кеширования браузерами вы не можете, придётся плясать по их правилам. А для них 2хх — это коды годных результатов.
вы, пардон, описываете принципы построения REST JSON API, а не «особенности построения REST JSON API для браузеров».
там может быть и мобильный клиент, и интернет вещей как клиент — да кто угодно.
да и с кешированием 200х ответов по API браузером вы, мягко говоря — лукавите.
Повлиять на политику кеширования браузерами вы не можете
А Cache-Control на что?
По науке автор прав — не надо прогибаться, есть стандарт.
На практике, конечно, выйдет немного по-другому — если есть нужда работать через такой прокси в такой позе — будет работать, или у тебя, или у следующего разработчика.
Значит, сервер должен иметь lame duck mode специально для такого случая, который активируется или дополнительным хедером, или параметром, или протоколом, или просто по IP.
Чего совершенно нельзя делать, так это брать нестандартные решения и делать из стандартными — суть стандартов в том, что они более-менее универсальны.
С тем же успехом может прийти какой-нибудь камрад с монгольского прокси и выдвинуть несовместимые требования — мол, у нас HTTP 200 считаются трафиком, а остальные нет, давайте 204 вместо 200.
Вы когда-нибудь пытались сделать клиента к апи, которое на всё подряд кидает 200 ОК?
Легко. HTTP — это всего лишь транспорт, и 200 обозначает что ответ получен от приложения, а не кого-то ещё, следовательно, можно разбирать тело, а уже в нём всё написано.
Если это не 200, значит, проблема с сервером, прокси или что там ещё по пути к приложению, и разбирать тело смысла не имеет (так как оно не от приложения). Можно возвращать 4xx для того что можно валидировать до приложения (формат, авторизацию и т.п.), но не более того — сам запрос (правильный json) должен обрабатываться только приложением, и только оно может на него ответить.
Изоляция приложения от транспорта полезна ещё и тем что можно легко переключится на любой другой транспорт без риска потерять обработку ошибок, причём не меняя ничего в самом приложении со клиентской стороны.
К примеру, Cloudflare API использует именно этот метод — и это весьма удобно, там вообще нет ответов кроме 200 и 301 (все 4xx это проблемы с запросами ещё до того как они попадают в приложение).
Натягивание кодов ошибок HTTP на возможные ситуации с приложением (которых может быть на порядок больше чем в HTTP, не говоря уже о том что ошибок может быть больше одной и разного типа, и все их нужно обработать) — плохая практика.
При обработке сложных запросов также возникает необходимость возвращать более чем одну ошибку (особенно если запрос обрабатывает более одного объекта), причём разного рода — и все их нужно обработать. Простой пример — пользователь отправил форму, в ней с десяток полей, некоторые с ошибками — будет весьма логично вернуть список всех ошибочных полей за раз, а не останавливаться на первом и возвращать их по мере того как он их исправляет, при этом ошибки могут быть разными («неверный формат», «недопустимое значение» etc), это всё не влезает в линейку 4xx.
Ещё более жесткий случай если мы создаем несколько объектов в одном запросе (batch processing), при этом допускается что не все они будут созданы (частичный успех) — тут 201 никак не поможет, а попытка создание объекта который уже есть (и может быть только в одном экземпляре) вообще никак не отображаема в HTTP — можно было бы использовать 409, но тогда это неотличимо от других конфликтов. В общем, много таких вещей, которые в HTTP не влезут при всём желании, без изобретения своих кодов.
Пример плохого дизайна — в PowerDNS API — на всё что сервер не понял он возвращает «422 Unprocessable Entity», а в теле — текстовое описание проблемы, которое совсем почти непарсабельно (да и может измениться в любой момент, ибо даже не документировано).
Но разумеется, если у вас приложение само является endpoint, перед ним нет даже хиленького LB, а все возможные ошибки «влезают» в 4xx/5xx — тогда да, в самый раз. Увы, в реальности такое бывает редко.
Вопрос в том, что отказываясь от нормальной работы с HTTP, вы также отказываетесь от большей части инфраструктуры (балансировщики, кэши, прокси, мониторинг, etc). На всё можно свои велосипеды наколхозить, только зачем? Тогда уж лучше взять какой-нибудь стандартный RPC типа SOAP, где есть своя инфраструктура.
Честно говоря, я не вижу каким образом использование HTTP только как транспорта (как я описал) заставляет отказываться от инфраструктуры, и одновременно сильно упрощает как клиента, так и сервер, потому что весь цикл обработки примерно такой:
// Сервер
while (GetRequest()) {
ProcessRequest()
SendResponse()
}
// Клиент
while (HaveJobs()) {
SendRequest()
ProcessResponse()
}
и всё — код никак не зависит от транспорта, кроме Get/Send.
GET может кэшироваться как и обычно (если нужно и разрешено), остальное спокойно проходит через всё что угодно без потерь (при наличии «умного» прокси-кэша-LB тоже может ограниченно кэшироваться — даже PUT, PATCH, POST и DELETE), при этом клиент гарантированно знает что любой код отличный от 200 обозначает только одно — запрос не попал в приложение (что и требуется — в этом суть транспорта). Мониторинг тоже не страдает — просто может потребоваться добавить разбор тела (причём это стандартная возможность любого приличного мониторинга), но и это не всегда нужно — потому что (снова) — 200 означает «всё ок». Балансировщики — тоже в порядке, ибо если приложение отвечает 200 — значит оно доступно, для недоступности есть 5xx (или таймаут, или connection refused). Что ещё?
Я лично использую этот «велосипед» уже более 15 лет, за стеной прокси, LB и мониторинга, им очень активно пользуются несколько тысяч клиентов — никаких проблем, всё работает как часы (хотя в начале это был не JSON а XML, не SOAP, свой).
Да и пример с Cloudflare я не случайно привел — они на HTTP и всей инфраструктуре целую армию собак съели, но их собственное API использует HTTP только как транспорт, и это реально удобно, но если они захотят, то почти без усилий могут его сделать доступным хоть по почте (SMTP).
Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать
Кто привык к этому, добро пожаловать в GraphQL, там нет заморочек с кодами возврата, адресами ресурсов (точка входа вообще одна). Все очень просто устроено.
418 I'm a Teapot
Существует только в первоапрельском RFC 2324, то есть не включен ни в один стандарт. И даже по вашей логике его отличия от стандартного 400 Bad Request довольно неопределённые.
В дев/staging окружении 418 это железобетонный баг клиента, можно прям автоматом баг заводить в трекере.
На проде 418 означает, что клиент у нас хакер, спамер, сканер или фаззер, и его можно спокойно отдавать fail2ban или аналогу.
418 первоапрельский RFC, и поэтому не будет выкинут ни одним промежуточным софтомНе понял причинно-следственной связи: почему не будет?
RFC 2324 Status: INFORMATIONAL
Strict-реализация HTTP совершенно спокойно может отбрасывать коды вне стандарта.
Вы же не будете утверждать, что различный софт и железяки должны поддерживать вот это всё.
Можете аргументировать?
Но вообще, причина в ненужной ошибке, которая испугает пользователя, если произошел разрыв соединения в каком-нибудь тоннеле.
> Идемпотентный запрос, т.е. повторный DELETE с таким же адресом не приводит к ошибке 404.
Можете аргументировать?
Вы отправляете DELETE запрос на ресурс с ID 100.
Ресурс удалён? Удалён. Возвращаете информацию о том то удаление прошло успешно, вот и всё )
Можно ещё запариться с ключами идемпотентности. Хорошая статья была совсем недавно — habr.com/ru/company/yandex/blog/442762
Всё, описанное здесь, конечно хорошо и здорово. Наверное, стоит прислушаться к полезным пунктам этого поста, но не нужно путать теплое с мягким.
REST API это всего лишь два слова, не несущие никаких четких стандартов. Никто не обязывает использовать HTTP или не использовать "велосипеды со всякими {error: "message","result":...}
".
Соответственно, все пункты, касаемые того, что должны делать сервер и клиент (заголовки Accept, тип text/plain при ошибке, использование множества кода ошибок вместо 200/400/500, в крайнем случае — 404, причем зачастую люди трактуют их неверно, привет коду 418), это не стандарт, а пожелание автора, пусть хорошо понятное и в целом для многих справедливое. Нужно помнить об этом.
Стандарт HTTP это стандарт. Его несоблюдение вредно для кармы и ведёт к постоянным проблемам с безопасностью, кэшированием и прочими «закидонами» браузеров, которые совсем не закидоны, а просто следование стандарту.
Непонятно, каким образом это относится к REST API. Это относится к любому неправильно спроектированному API, нет разве?
Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать
Не могли бы раскрыть свою мысль?
Поддержка большим количеством готовых клиентских библиотек на все случаи жизни. Те, кто будет вашим api пользоваться, скажут большое человеческое спасибо.
Тоже интересно узнать подробности, о чём конкретно идёт речь?
Поддержка автоматизированного интеграционного тестирования. Когда сервер на любые запросы отдаёт 200 ОК — ну, это такое себе развлечение.
Ну так надо смотреть тело, разве нет?
Вообще, натягивание статусов протокола на приложение, как по мне, не самое лучше решение.
Если я отправляю запрос на получение сущности, а возвращается 404, что это? Страница не найдена вообще или сущность не найдена? Как это различать?
Или, например, есть запрос на получение данных, который принимает большое количество аргументов. Например, сложный фильтр, который может быть совершенно произвольной формы и не ограничен по размеру. Что в этом случае делать?
Т.е. в целом, я не против REST API, но это всего лишь соглашение, не всегда удобное, не стоит его навязывать всем.
Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать
Почему вы считаете это велосипедом? Microsoft API
Если клиенту нужно отобразить ошибку? Даже тривиально — результат валидации?
Побочным бонусом от этого оборачивания становится возможность докинуть информацию о пагинации не в заголовки.
Про побочный бонус — категорически согласен.
Побочным бонусом от этого оборачивания становится возможность докинуть информацию о пагинации не в заголовки
С этим соглашусь. Добавил в статью.
Если вы используете защиту от CSRF (а лучше бы вам её использовать), то удобнее передавать CSRF-токен в отдельном заголовке (типа X-CSRF-Token) для всех запросов, а не внедрять вручную в каждый запрос. Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.
Это не верно.
Во-первых, суть CSRF атаки не состоит в том, чтобы украсть кукисы. Она состоит в том, что приложение злоумышленника шлет запросы от лица пользователей в другое веб-приложение.
Во-вторых, кукисы с CSRF-токенами нельзя украсть, если в браузере отсутствуют уязвимости. Вся суть защиты от CSRF-атак строится на том, что кроме оригинального сайта ни у кого больше нет доступа прочитать содержание кукисов.
В-третьих, никто не использует кукисы с CSRF-токенами без хеадеров. В этом нет никакого смысла. Поскольку кукисы проставляются и отправляются браузером автоматически, то они не дают сами по себе никакой защиты. Сайт злоумышленника, попытавшись отправить запрос от имени пользователя, успешно это сделает, если кроме кукисов ничего нет. Поскольку браузер проставит все кукисы сам.
Вся суть защиты состоит в том, что клиент отправляет одновременно токен и в хеадере, и в кукисах. И этот токен должен совпадать.
Самый простой пример — проверка никнеймов или страниц, связанных с ними. И если мы заходим на несуществующий никнейм — мы видим 404 и кнопку «Занять никнейм», а если кто-то уже когда-то занял его, а затем удалил свою страницу (прямо как в ВК), то кнопка «Занять никнейм» уже не появляется.
Браузеры обрабатывают http-коды по-разному. Множество инструментов по-умолчанию настроены на стандарт и отвечают соответственно.
Коду тоже нужно. Когда вы используете самый стандартный `fetch API` в браузере, он учитывает коды и выдает TypeError в случае 4xx/5xx
Метод OPTIONS, тоже как часть стандарта, определяет возможности сервера и участвует в CORS.
P.S. Junior'ы, ключевое слово сообщения сверху — «по мне».
И вас будет двухуровневый код обработки ошибок — сначала смотрим http статус, а затем пытаемся распарсить тело ответа. Вопрос — а что это даст? В чём смысл?
сначала смотрим http статус, а затем пытаемся распарсить тело ответа.
Да разные случаи бывают, например возьмем код:
408 Request Timeout
Сервер предположить, когда следующий запрос может быть обработан и переслать в теле эту информацию, чтобы не «дидосить» API или не заставлять пользователя ждать слишком долго.
Да и вообще коды 4xx в стандарте описываются так, что они могут отражать некоторую детализацию ошибок:
Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method
* При создании (апдейте) объекта, не вижу ничего плохого чтобы вернуть этот самый созданный\обновленный объект чтобы сократить таким образом коммуникацию и избежать доп. нагрузки когда всегда следом прилетит GET на тот же объект
* Считаю что очень нужно и правильно при 4хх ошибках возвращать в JSON доп информацию по ошбке. Как то: код\константу ошибки. Сопровождающий текст. Возможно какую то информацию requestUUID и.т.д. Возвращать тело ошибки текстом когда вся остальная система работает с JSON — как минимум странно
Так же в статье деликатно обошли множество моментов, когда при формировании API сложно создать правильный URI с точки зрения операций над объектами (операции не являющиеся созданием или получением ентити и какие-то промежуточные операции).
При создании/обновлении объекта методом PUT ни ответа, ни последующего GET не требуется: ведь новое содержимое объекта должно соответствовать переданному с точностью до форматирования.
А вот в ответ на запрос PATCH должен возвращаться полученный объект, и об этом написано.
Это не всегда так. Если, допустим, сервер добавил какие-то поля при создании объекта (например, дата создания или автоматически сгенерированный uuid), то отдельный гет на каждый такой пост выглядит избыточным. Проще и дешевле возвращать объект в ответе целиком.
Конкретно в описанном варианте чистого REST-CRUD метод PUT должен использоваться только в том случае, когда объект передаётся на сервер целиком, а сервер не имеет права добавлять к нему никаких дополнительных значимых полей:
A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response. — RFC 7231, 4.3.4 PUT
Если передаётся лишь часть полей — надо использовать PATCH. Или перестать играть в REST и использовать обычный POST.
Как вариант, можно выделить субресурс с частью полей и делать PUT для него.
Как отправлять файлы на сервер тут написано, а как скачивать через api — нет. Если следовать всем этим рекомендациям в лоб, то придётся, того и гляди, кодировать файлы в base64 и отправлять на клиент как строку json.
Мне почему-то кажется, что для части api, работающей с файлами, допустимо использование любых Content-Type. И каждый файл нужно передавать под его "родным" Content-Type, а не как-то ещё.
Неидемпотентность POST может оказаться проблемой. Решением может стать генерация id на стороне клиента и создание сущности методом PUT.
Описание ошибки в text/plain не всегда хорошая идея. Например, если разные ошибки требуется обрабатывать по-разному, а кодов HTTP не хватает. Или если предполагается перевод сообщений об ошибках на стороне клиента.
У вас не происходит управления состоянием клиента со стороны сервера, вы просто предоставляете эндпоинты для вызова процедур. Вот даже возмущение Роя Филдинга по этому поводу, хотя вы видимо, о нем, даже не слышали. Уточню, Рой Филдинг — это человек который ввел понятие REST в своей диссертации в 2000 году.
И понятие это обозначает наложение некоторых ограничений на архитектуру системы:
1. Starting with the Null Style — клиенту не нужно знать про сервер абсолютно ничего кроме одной единственной точки входа, все дальнейшие возможности для переходов идут с сервера. В полноценных REST API, если взять например те REST API что возвращают HTML как гипертекст (см. HATEOAS — hypertext as the engine of application state), это будут теги и теги . У вас же просто кучка ендпойнтов которые клиент уже должен знать.
2. Client-server и Separation of Concerns
A proper
separation of functionality should simplify the server component in order to improve
scalability. This simplification usually takes the form of moving all of the user interface
functionality into the client component. The separation also allows the two types of
components to evolve independently, provided that the interface doesn’t change.
Ваш клиент и сервер могут бесконечно масштабироваться и изменяться(развиваться) независимо. Как, например, Google не нужно обновлять браузер Chrome который представляет собой клиент каждый раз когда обновляется какая-то страничка в интернете, все что требуется от клиента и сервера — обмениваться данными в том формате которые они оба понимают(см. Unified Interface), функциональность клиента так же может быть расширена не обязательным 7 пунктом (Code on demand)
3. Stateless. Отсутствие состояния соединения. Благо хотя бы до нарушения этого пунктика статья ещё не дошла.
4. Кэширование. В статье не упоминалось, но напомню что это один из 6 пунктиков который должен обязательно выполняться чтобы вы могли назвать своё API REST
5. Uniform Interface. То, что я частично описал в п.2… У вас клиент не должен знать что определенный эндпоинт вернет определенную структуру данных. Чтобы обеспечить масштабируемость и независимую разработку, устраняется любой Coupling между клиентом и сервером, и всё что умеет клиент — отображать стандартные форматы данных(как браузер отображает html, jpeg, etc.).
Чтобы получить единый интерфейс системы накладываются так же такие ограничения как:
— Идентификация ресурсов;
— Выполнение действий над ресурсами через представления;
В вашем же случае идет просто обмен заранее известными структурами данных(Привет RPC).
6. Многослойная система(не затрагивается в рамках этой статьи).
7. Code on Demand — возможность загрузки кода с сервера в виде апплетов или скриптов для расширения функциональности клиента — например научить понимать его новые форматы данных.
Вот эти вот принципы, перечисленные выше, являются принципами построения REST API, а не урлы красивенькие сделать поверх JSON RPC.
Вся ваша статья лишь о том как должен выглядеть http запрос и какие должны быть урлы, только вот «REST ignores the details of component implementation and protocol syntax to focus on the roles of components, the constraints upon their interaction»(с) Dissertation
И нет, это не плохо, и не хорошо, это просто разные архитектуры, стили написания API. REST он о том, как функционировал web без этих ваших громоздких SPA на JS, REST накладывает ограничения которые могут быть вам не нужны. Реализовать мобильный REST-клиент это по сути изобрести новый мобильный браузер. Возможно, правда, за основу брать не HTML.
Просто не нужно подменять понятия, да и ещё пытаться учить этому новичков.
На русском советую почитать хотя бы это — habr.com/ru/post/319984.
На английском — диссертация и roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
Если речь идёт о чём-то кроме гипертекста, а клиент является чем-то кроме браузера, то клиент в любом случае должен понимать схему данных, хотя бы минимально.
А если клиент работает без участия пользователя — то без строгой схемы данных ну никак не обойтись.
Если речь идёт о чём-то кроме гипертекста, а клиент является чем-то кроме браузера, то клиент в любом случае должен понимать схему данных, хотя бы минимально.
Без проблем, только REST-ом это не называйте. Я упомянул что реализовывать свой кастомный REST клиент вместо существующего(в виде браузера), весьма затратно.
В случае если ваш клиент описыват лишь стандартные типы данных, которые он умеет принимать, а все данные, и информация о том как их правильнее отобразить приходит с сервера, ничего про структуры знать не нужно.
Но в таком случае зачем вообще придуман термин REST? Для интерфейса «как в браузере» уже есть название — «браузер». И для протокола HTTP тоже есть название — HTTP.
REST был придуман 19 лет назад, и тогда он действительно был нужен, и успешно решал некоторые проблемы. А Рой Филдинг является одним из авторов спецификации HTTP, так что их схожесть, точнее то как он удобно ложиться на HTTP вовсе не случайное совпадение )
Взаимодействие клиентов и серверов в браузере действительно строилось и строится по REST архитектуре, то что это не особо укладывается в архитектуру модных нынче SPA, или не удовлетворяет потребности мобильных разаботчиков, решает проблемы которых их не интересуют не обозначает что нужно обобщать термин REST до уже существующего RPC. Ещё раз повторюсь, RPC — не плохо. RPC это другой стиль решающий другие проблемы, если он решает ваши проблемы, а он решает проблемы большинства, RPC — это хорошо.
Ну всё же REST он не о протоколе передачи данных, а о взаимодействии компонентов.
Вы сейчас занимаетесь тем же самым в чём обвиняете оппонентов: вольным расширением терминов. RPC — это, конечно же, хорошо — вот только всё что описано в обсуждаемой статье нормальным RPC не является.
Допустим, у вас бложик. Есть три типа сущностей — записи, комментарии и юзеры. Это три разных ресурса, у которых должно быть три разных URI. Никто не предлагает свалить это всё в одну кучу с одной точкой входа. Клиент, собирающийся получить ресурс, должен знать URI этого ресурса. Это фундаментальная основа всего стиля REST.
Как оно там на сервере будет обрабатываться — одной точкой входа, которая ловит всё, или отдельным файлом на каждый эндпоинт — это дело сервера, знать о котором клиенту не обязательно.
Второй столп — полное единообразие на уровнях OSI от транспортного до прикладного. Описанное в статье как раз этому и соответствует. Неважно какой запрос, с одной стороны запихиваем данные в формате JSON, с другой их же и достаём, не задумываясь об их преобразованиях под капотом.
Никаким RPC здесь и не пахнет.
Выше прикладного уровня REST не лезет и не должен, это уже слои бизнес-логики. И да, специализированные клиенты должны немного знать о бизнес-логике, на то они и специализированные.
Второй столп — полное единообразие на уровнях OSI от транспортного до прикладного. Описанное в статье как раз этому и соответствует. Неважно какой запрос, с одной стороны запихиваем данные в формате JSON, с другой их же и достаём, не задумываясь об их преобразованиях под капотом.
Никаким RPC здесь и не пахнет.
Выше прикладного уровня REST не лезет и не должен
REST это то что описано в документации Роя Филдинга, а описано там 7 конкретных архитектурных ограничений, не нужно выдумывать что-то своё и коверкать понятия.
Есть три типа сущностей — записи, комментарии и юзеры. Это три разных ресурса, у которых должно быть три разных URI.
Это один подпунктик из 7 архитектурных ограничений. Если вы прочитаете мой комментарий то поймете что речь шла о том что клиент про эти URI для начала взаимодействия с вашим сервером знать не должен. Точно так же как мне чтобы читать блог Мартина Фаулера не нужно помнить URI каждой его статьи, так и клиенту для начала взаимодействия с вашем API не нужно знать ничего кроме одной единой точки входа.
Уж лучше на эту, чем на тот диссер. 20 лет назад ни JSON, ни даже AJAX ещё не было.
Не делать REST API — хорошо. Никаких проблем.
Коверкать понятия — никода не хорошо.
Делать кривое АПИ и изобретать велосипеды — плохо.
Описанное в статье полностью соответствует всем принципам REST.
Как это соответствует как минимум приниципу HATEOAS, если у вас в API не отдаёт гипертекст?
roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
Делать кривое АПИ и изобретать велосипеды — плохо.
Да, плохо.
Как это соответствует как минимум приниципу HATEOAS
За 20 лет веб ушёл «немного» вперёд. Кроме того, здесь речь о JSON REST, а не HTML REST.
JSON REST, а не HTML REST.
Ссылочками на спецификации не поделитесь? А то пока что стойкое ощущение что определения вы выдумываете на ходу
А ещё — «REST ignores the details of component implementation and protocol syntax to focus on the roles of components, the constraints upon their interaction»
В реальном мире большинство REST-сервисов не являются таковыми стопроцентно с академической точки зрения, и это нормально, так как они эффективно решают задачи, ради которых создавались. Такой подход можно назвать "Pragmatic REST". Более того, существует модель зрелости Ричардсона, в которой HATEOAS отмечен как то, к чему следует стремиться, но не то, что обязательно должно быть изначально, чтобы считаться REST (https://martinfowler.com/articles/richardsonMaturityModel.html). Так что в реальном мире выкрики в стиле "Или делайте 100% полностью как описано у Филдинга, или не называйте это REST!" считаю неуместными.
Очередной велосипед. Зачем? Есть стандарт OData.
5 версий? Я знаю только 2. И то, последняя содержит не изменения, а дополнения. Да и вообще, от версии к версии стандарт не изменяется, а только дополняется.
RFC 7231 specifies that a DELETE request may include a body, but that a server may reject the request.
Для знакомых с темой либо избыточно либо поверхностно, а для новичков не прояснилось ничего. Мне видится гораздо более перспективным описание языка декларации сервисов для любых категорий возможных читателей. Может быть я слишком пристрастен, конечно.
1. Что значит только UTF-8? Господин обдолбался пыхом? Что запросит клиент, то и будет, захочет US-ASCII — пожалуйста, или UCS-4 попросят, так держите на здоровье. И никакие «веселые» случаи тут не предусмотрены. Если ответ в json — он соответствует RFC 7159, все, шаг влево-вправо,
2. Где хранить CSRF-Token нигде не определено. И не просто так. Заявлять, что в куках нельзя никак хранить, странно. Если утащили куки, то компрометация CSRF — это меньшая из проблем (хотя здесь я скорее согласен, нечего в куки пихать то, что должно одну конкретную страницу защищать, проще на каждой странице отдельное скрытое поле выделять).
3. Про Uri — кэп, ты ли это? А ты точно освоил 100500 роутеров, чтобы так говорить?
4. Про HEAD запросы написана ересь, но частично пересекающаяся с правдой. Там все очень непросто, причем у каждого браузера свои тараканы — ответ тянет на целую статью, так что просто посмотрите как себя ведет гека с хед запросами и когда и зачем их делает и сделайте выводы (для экстрима можно edge пощупать, там вобще весело, и есть подозрение, что переход на хромиум градус веселья не уменьшит).
5. Обработка ошибок, вобще шик. А чегой-то она не может быть json или xml? У автора фобия на структурированные данные? Так в обычных ответах json его не смущал… Или у нас времена веб 1.0 и контент не сможет обработать json ответ с ошибкой? ой бяда, бяда.
Возможно, кому то коммент покажется слишком язвительным, но нет цели оскорбить автора. Скорее указать на однобокость его статьи. Может в обычном вебе это имеет ценность, но, например, в энтерпрайзе — за утверждение только UTF-8, только мейнстрим — бьют по голове, иногда даже кирпичом. Когда нужно подружить десяток разных сервисов — REST привлекает именно своей гибкостью и в то же время унифицированностью помноженной на неплохую возможность документирования (описать что делает эндпоинт, не сложно) и главное, сочетающей неплохую надежность.
Мало технологий позволяют сочетать в себе столь противоречивые качества. И тем сложнее дать им вменяемое описание, коль скоро они не имеют RFC. Автор замахнулся на неблагодарное дело…
С остальным даже не знаю с чем спорить. Это долбаный туториал для тех, кто до сих пор первым делом отдаёт 200 ОК и вешает действия на get-запросы. Как бы количество добавлений в закладки намекает, да?
Рано вам туториалы писать. Тем более пытаться загнать REST — в искусственные рамки. Они есть, но несколько другие, чем описанные в статье.
Зато есть куча языков, систем и просто софта, у которого большие проблемы с кодировками, особенно с не-UTF кодировками.
Также желаю вашему дотнету с явой успехов при работе в однобайтной кодировке с базой, содержащей неанглоязычные символы (а да, мир не только на английском разговаривают). Предположим, вам нужно поискать CITROЁN. Любое перекодирование, даже из UTF-8 в UTF-16, создаст очень большие и весёлые грабли.
Также желаю вашему дотнету с явой успехов при работе в однобайтной кодировке с базой, содержащей неанглоязычные символы (а да, мир не только на английском разговаривают). Предположим, вам нужно поискать CITROЁN. Любое перекодирование, даже из UTF-8 в UTF-16, создаст очень большие и весёлые грабли.
Видимо вы очень плохо знакомы с языками общего назначения. Ни для дотнета, ни для явы вобще не составит никакого труда работать с любой базой в любой кодировке, при наличии драйвера, конечно. Более того — разница будет лишь в паре строчек в конфиге и то только в отдельных, тяжелых, случаях.
Да и вобще, как можно приплести архитектуру(!) взаимодействия к базам данных?
Впрочем, делайте что хотите — хоть rest без http, хоть перекодирование в Latin-1.
Например, у сущности User есть поле locked типа boolean. На запрос клиента под логином администратора сервис отдаёт и принимает сущность, обновляя её полностью. При запросе клиентом под учёткой пользователя сервис не может не глядя принимать и обновлять сущность User, так как у пользователя нет прав на редактирование поля locked.
— написать для каждой сущности класс-обёртку;
— создать кастомный провайдер сущностей для запросов пользователя;
— создать отдельные URI для пользователя и администратора.
Ах да, вы ссылаетесь на спеку по HTTP. Да вот незадача: та спека совсем не про REST. И не про JSON. И не про то, как правильно завернуть произвольный RPC API в связку HTTP+JSON в качестве транспорта.
Принципы построения REST JSON API
При этом описание ошибки, если оно есть, приводится в теле ответа в формате text/plain (без всякого JSON).
Я не любитель апишек, которые не используют HTTP коды для ошибок, но для JSON API лучше уж 200 и JSON с индикатором ошибки в теле, чем 400 и строка.
418 I'm a Teapot
Я думал первое апреля уже прошло, с каких пор 400(и 404 в случае неверных урлов) недостаточно?
Парсить url регулякой — не лучший выбор
Я 2 года работал с magento 2 через rest api. Познал всю боль которую только можно было представить. Это с тем учетом что api довольно продумано и выражено в прекрасном swagger. Тысячи полей и мегабайты данных и кучи условий, выборок, коллекций.
Довольно забавно потом читать учения как надо сделать 2+2 и отправить очередное name в /product.
Принципы построения REST JSON API