Эта памятка писалась для внутренних нужд (открыть глаза менее опытным в вебе коллегам). Но, т.к. я насмотрелся велосипедов от довольно уважаемых, казалось бы, контор, — выкладываю на хабр. Мне кажется, многим будет полезно.


Зачем


Надеюсь, читающий уже понимает, зачем ему вообще нужен именно REST api, а не какой-нибудь монстр типа SOAP. Вопрос в том, зачем соблюдать какие-то стандарты и практики, если браузеры вроде бы позволяют делать что хочешь.


  • Стандарт HTTP это стандарт. Его несоблюдение вредно для кармы и ведёт к постоянным проблемам с безопасностью, кэшированием и прочими "закидонами" браузеров, которые совсем не закидоны, а просто следование стандарту.
  • Велосипеды со всякими {error: "message","result":...} невозможно нормально тестировать и отлаживать
  • Поддержка большим количеством готовых клиентских библиотек на все случаи жизни. Те, кто будет вашим api пользоваться, скажут большое человеческое спасибо.
  • Поддержка автоматизированного интеграционного тестирования. Когда сервер на любые запросы отдаёт 200 ОК — ну, это такое себе развлечение.

Структура запросов и ответов


Любой http-запрос начинается со строки


METHOD URI


где METHOD — это метод доступа (GET, PUT и т.д.), а URI — адрес запрашиваемого ресурса.


В начале запроса идут заголовки — просто текстовые строки вида key: value
Затем передаётся пустая строка, означающая конец секции заголовков, и затем — тело запроса, если оно есть.


В ответе сначала передаётся строка с версией http, кодом и строковым статусом ответа (например HTTP/1.1 200 OK), далее текстовые заголовки ответа, потом пустая строка, потом тело ответа.


Тут вроде всё просто.


Кодирование запросов и ответов


Кодировка для всех и запросов, и ответов — UTF-8 и только UTF-8, т.к. некоторые, кхм, "браузеры" имеют привычку игнорировать содержимое заголовка charset.


Использование кодов символов и html-сущностей не допускается, т.е. режим JSON_UNESCAPED_UNICODE обязателен. Не все клиенты знают всю таблицу html сущностей (типа каких-нибудь ù ), да и при чём тут html. Не все клиенты готовы/хотят заниматься перекодированием \uXXXX; и &#XX;. Плюс возможны "весёлые" ситуации с избыточным экранированием или пропаданием слэшей и амперсандов.


Все данные, кроме URI и двоичных файлов, передаются в формате JSON. Обратите внимание, что далеко не всякий валидный javascript код является валидным JSON.
В частности, для строк используются только двойные кавычки. Одинарные кавычки в json-данных, хотя и допустимы в "обычном" javascript, могут вызвать непредсказуемые плохо отлавливаемые баги.


В запросах обязательно указывается заголовок


Accept: application/json, */*; q=0.01

Вызовы к API отличаются от прочих вызовов (например, обычной загрузки html страницы по данному URI) именно по наличию application/json в Accept.


Сама строка Accept формируется браузером/клиентом и может немного отличаться от браузера к браузеру, например, наличием и других форматов типа text/javascript, поэтому нужно проверять не равенство, а именно вхождение "application/json".


В ответах 2хх с непустым телом обязательно наличие заголовка ответа


Content-Type: application/json; charset=UTF-8

При наличии тела запроса также обязателен заголовок запроса


Content-Type: application/json; charset=UTF-8

либо, при загрузке файлов,


Content-Type: multipart/form-data

и далее, в первой части


-----------------
Content-Type: application/json; charset=UTF-8
Content-Disposition: form-data; name="data"

после чего для каждого файла


-----------------
Content-Type: image/jpeg
Content-Disposition: form-data; name="avatar"; filename="user.jpg"

Если вы используете защиту от CSRF (а лучше бы вам её использовать), то удобнее передавать CSRF-токен в отдельном заголовке (типа X-CSRF-Token) для всех запросов, а не внедрять вручную в каждый запрос. Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.


Структура URI


Нагородить можно всякое, но лучшая практика — чтобы все URI имели вид


/:entity[/:id][/?:params]


ну, или если у вас api лежит в какой-то папке,


/api/:entity[/:id][/?:params]


Здесь:


  • entity — название сущности, например, класса или таблицы/представления в БД. Примеры: users, dictionary
  • id opt. — первичный ключ объекта. Если первичный ключ составной, то части указываются через слэш. Примеры: /users/10, /dictionary/ru/apptitle
  • params opt. — дополнительные параметры выборки для списочных запросов (фильтрация, сортировка, паджинация и пр.). Форматируются по правилам HTTP GET параметров (функции encodeURIComponent и пр.)

Для разбора части URI до знака вопроса можно использовать регулярку


#^/(<entity>([a-z]\-_)+)/?(<id>([a-z][A-Z][0-9]\-_/)*)?$#

Ведущий слэш обязателен, т.к. неизвестно, с какого URL будет осуществлён запрос.


Методы HTTP


GET /:entity/:idgetById


В случае успеха сервер возвращает 200 OK с полями объекта в формате JSON в теле ответа (без дополнительного оборачивания в какой-либо объект)


В случае, если объект с такими id не существует, сервер возвращает 404 Not Found


В ответе обязательно должны быть заголовки, касающиеся политики кэширования, т.к. браузеры активно кешируют GET и HEAD запросы. При остутствии какой-либо политики управления кэшем должно быть:


Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache

GET /:entity[?param1=...&param2=...] — списочный get


Простой случай: в случае успеха сервер возвращает 200 OK с массивом объектов в формате JSON в теле ответа (т.е. ответ начинается с [ и заканчивается ]).


Если массив получился пустой, всё равно вовзращается 200 OK с пустым масивом [] в теле ответа.


Более сложный вариант: возвращается объект, в одном из полей которого — искомый массив. В остальных полях — данные о пагинации, фильтры, счётчики и пр. Только держите это консистентным по всем api.


HEAD /:entity[/:id] — запрос заголовков


Полный аналог GET с таким же URI, но не возвращает тело ответа, а только HTTP-заголовки.


Реализация поддержки HEAD запросов веб-сервером обязательна.


Активно используется браузерами в качестве автоматических pre-flight запросов перед выполнением потенциально опасных, по их мнению, операций. Например, браузер Chrome активно кидается head-запросами для получения политик CORS при кросс-доменных операциях (виджеты и пр). При этом ошибка обработки такого head-запроса приведёт к тому, что основной запрос вообще не будет выполнен браузером.


Может использоваться для проверки существования объекта без его передачи (например, для больших объектов типа мультимедиа-файлов).


POST /:entity — создаёт новый объект типа :entity


В теле запроса должны быть перечислены поля объекта в формате JSON без дополнительного заворачивания, т.е. {"field1":"value","field2":10}


В случае успеха сервер должен возвращать 201 Created с пустым телом, но с дополнительным заголовком


Location: /:entity/:new_id


указывающим на месторасположение созданного объекта.


Возвращать тело ответа чаще всего не требуется, так как у клиента есть все необходимые данные, а id созданного объекта он может получить из Location.


Также метод POST используется для удалённого вызова процедур (RPC), в этом случае ответ будет иметь статус 200 OK и результаты в теле. Вообще смешивать REST и RPC в одном api — идея сомнительная, но всякое бывает.


Единственный неидемпотентный некешируемый метод, т.е. повтор двух одинаковых POST запросов создаст два одинаковых объекта.


PUT /:entity/:id — изменяет объект целиком


В запросе должны содержаться все поля изменяемого объекта в формате JSON.


В случае успеха должен возвращать 204 No Data с пустым телом, т.к. у клиента есть все необходимые данные.


Идемпотентный запрос, т.е. повторный PUT с таким же телом не приводит к каким-либо изменениям в БД.


PATCH /:entity/:id — изменяет отдельные поля объекта


В запросе должны быть перечислены только поля, подлежащие изменению.


В случае успеха возвращает 200 OK с телом, аналогичным запросу getById, со всеми полями изменённого объекта.


Используется с осторожностью, т.к. два параллельных PATCH от двух разных клиентов могут привести объект в невалидное состояние.


Идемпотентный запрос.


DELETE /:entity/:id — удаляет объект, если он существует.


В случае успеха возвращает 204 No Data с пустым телом, т.к. возвращать уже нечего.


Идемпотентный запрос, т.е. повторный DELETE с таким же адресом не приводит к ошибке 404.


OPTIONS /:entity[/:id]


Получает список методов, доступных по данному URI.


Сервер должен ответить 200 OK с дополнительным заголовком


Allow: GET, POST, ...

Некешиуремый необязательный метод.


Обработка ошибок


Возвращаемые ошибки передаются с сервера на клиент как ответы со статусами 4хх (ошибка клиента) или 5хх (ошибка сервера). При этом описание ошибки, если оно есть, приводится в теле ответа в формате text/plain (без всякого JSON). Соответственно, передаётся заголовок ответа


Content-Type: text/plain; charset=UTF-8

Использовать html для оформления сообщений об ошибках в api — так себе идея, будут проблемы журналированием и т.д. Предполагается, что клиент способен сам красиво оформить сообщение об ошибке для пользователя.


При выборе конкретных кодов ошибок не следует слишком увлекаться и пытаться применить существующие коды только потому, что название кажется подходящим. У многих кодов есть дополнительные требования к наличию определённых заголовков и специальная обработка браузерами. Например, код 401 запускает HTTP-аутентификацию, которая будет странно смотреться в каком-нибудь приложении на react или electron.


UPD по мотивам комментариев. Клиенты у вас будут разные: не только веб и мобильные приложения, но и такие штуки, как запускалка интеграционных тестов (CI), балансировщик нагрузки или система мониторинга у админов. Использование или неиспользование того или иного статуса ошибки определяется тем, будет ли он полезен хоть какому-то клиенту (т.е. этот клиент сможет предпринять какие-то действия конкретно по этому коду) и, наоборот, не будет ли проблем у какого-то из клиентов из-за неиспользования вами этого кода. Придумать реальный use-case, когда реакция клиента будет различаться в зависимости от 404 или 410, довольно сложно. При этом отличий 404 от 200 или 500 — вагон и телега.


400 Bad Request


Универсальный код ошибки, если серверу непонятен запрос от клиента.


403 Forbidden


Возвращается, если операция запрещена для текущего пользователя. Если у оператора есть учётка с более высокими правами, он должен перелогиниться самостоятельно. См. также 419


404 Not Found


Возвращается, если в запросе был указан неизвестный entity или id несуществующего объекта.


Списочные методы get не должны возвращать этот код при верном entity (см. выше).


Если запрос вообще не удалось разобрать, следует возвращать 418.


415 Unsupported Media Type


Возвращается при загрузке файлов на сервер, если фактический формат переданного файла не поддерживается. Также может возвращаться, если не удалось распарсить JSON запроса, или сам запрос пришёл не в формате JSON.


418 I'm a Teapot


Возвращается для неизвестных серверу запросов, которые не удалось даже разобрать. Обычно это указывает на ошибку в клиенте, типа ошибки при формировании URI, либо что версии протокола клиента и сервера не совпадают.


Этот ответ удобно использовать, чтобы отличать запросы на неизвестные URI (т.е. явные баги клиента) от ответов 404, у которых просто нет данных (элемент не найден). В отличие от 404, код 418 не бросается никаким промежуточным софтом. Альтернатива — использовать для обозначения ситуаций "элемент не найден" 410 Gone, но это не совсем корректно, т.к. предполагает, что ресурс когда-то существовал. Да и выделить баги клиента из потока 404 будет сложнее.


419 Authentication Timeout


Отправляется, если клиенту нужно пройти повторную авторизацию (например, протухли куки или CSRF токены). При этом на клиенте могут быть несохранённые данные, которые будут потеряны, если просто выкинуть клиента на страницу авторизации.


422 Unprocessable Entity


Запрос корректно разобран, но содержание запроса не прошло серверную валидацию.


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


Обычно это означает ошибку в введённых пользователем данных, но может также быть вызвано ошибкой на клиенте или несовпадением версий.


500 Internal Server Error


Возвращается, если на сервере вылетело необработанное исключение или произошла другая необработанная ошибка времени исполнения.


Всё, что может сделать клиент в этом случае — это уведомить пользователя и сделать console.error(err) для более продвинутых товарищей (админов, разработчиков и тестировщиков).


501 Not Implemented


Возвращается, если текущий метод неприменим (не реализован) к объекту запроса.




Ну вот, в общем-то, и всё. Спасибо за внимание!

Комментарии (206)


  1. soon
    09.04.2019 06:57

    Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.

    Поправьте меня если я ошибаюсь, но суть CSRF не в воровстве куков. Если мы можем украсть куки, то нам не страшны никакие CSRF токены, потому что мы просто будем использовать куки, а токен нам пришлет сервер.


    1. vanxant Автор
      09.04.2019 07:07

      Не совсем так. GET и POST запросы можно подделать средствами чистого HTML, GET так вообще картинкой (типа <img src="..."), например, в почте (email fishing). И запрос будет выполнен браузером с текущими куками юзера. С POST чуть сложнее, не все емейл-клиенты пропустят форму, плюс нужна активная реакция от юзера, но всё равно можно. PUT и прочие запросы можно запилить только на js, который в большинстве случаев будет надёжно зарезан.
      CSRF токен при грамотной реализации позволяет защититься от атак типа вставки картинки в письмо. Но как его грамотно реализовать? Если мы лепим его в куку или в параметр (т.е. в url для get), то токен должен протухать при каждом новом запросе, что не очень удобно, когда клиент запускает запросы асинхронно целыми пачками. Передача дополнительного заголовка позволяет продлить жизнь токенам, плюс требует обязательного использования javascript или иного кода клиентом. И при этом может спасти, даже если клиенту подсунули трояна с curl, имеющего возможность прочитать живую куку прямо с диска.


      1. soon
        09.04.2019 07:19

        Я говорил не про важность использования CSRF в целом, а про конкретную цитату


        куки можно украсть, в чём собственно и состоит суть CSRF атаки.

        Для эксплуатации CSRF нет необходимости красть куки.


        CSRF токен при грамотной реализации позволяет защититься от атак типа вставки картинки в письмо.

        То есть атаку на GET? Если GET не меняет состояние, то CSRF для него не нужен.


        И при этом может спасти, даже если клиенту подсунули трояна с curl, имеющего возможность прочитать живую куку прямо с диска.

        Но как? У нас есть кука, которую достали трояном. Мы с этой кукой идем на сайт и все, никакой CSRF токен нам больше не нужен.


        1. vanxant Автор
          09.04.2019 07:28

          То есть атаку на GET? Если GET не меняет состояние, то CSRF для него не нужен.


          Нужен, если передаются конфиденциальные данные, которые мы не хотим отдавать кому попало.

          Но как? У нас есть кука, которую достали трояном. Мы с этой кукой идем на сайт и все, никакой CSRF токен нам больше не нужен.

          Идёте на сайт со свежеворованной кукой и на любой, в том числе get запрос получаете 419 Authentication Timeout, потому как CSRF токена у вас нет. Для продолжения работы вам нужно ввести пароль, который трояну украсть негде.
          Украсть живой токен конечно тоже можно, но уже значительно более сложным трояном (со сниффером и подделкой https сертификатов или чтением памяти браузера).


          1. soon
            09.04.2019 07:52

            Ок, принято.


          1. mayorovp
            09.04.2019 12:44

            Нужен, если передаются конфиденциальные данные, которые мы не хотим отдавать кому попало.

            А каким образом "кто попало" их получит? Рассмотрим ту же картинку с запросом, пришедшую на почту. Для пользователя она будет просто незагрузившейся картинкой, а злоумышленник её вовсе нигде не увидит.


          1. zabbius
            09.04.2019 15:06

            Не понятно. Тогда всякие там контактики и хабры должны меня выкидывать на авторизацию при переоткрытии браузера, а такого не происходит.
            Спертой куки хватит для авторизации, но не всегда есть возможность ее прочитать, иногда можно только вслепую ей воспользоваться (та самая картинка). И вот тут уже приходит CSRF токен в месте c Referer, которые тупо не дают сходу выполнить любой значимый запрос, кроме инициализации новой сессии (предъявляем куки) с получением CSRF токена. Как-то так.


            1. vanxant Автор
              09.04.2019 15:22

              Зависит от деталей реализации проверок CSRF и общего уровня садизма разрабов. При смене региона айпишника вконтактик вас выкинет и ещё и 2FA заставит делать.


          1. werevolff
            10.04.2019 03:58

            Добавьте, пожалуйста, в ваш список: Никогда не передавайте конфиденциальные данные методом GET.


            Далее, использование кукис при реализации CSRF Protection — корпоративный стандарт для многих фрэймворков и CMS.


  1. rpiontik
    09.04.2019 07:04
    +1

    Слабовата аргументация по использованию кодов ответов отличных от 200. Они не просто обрабатываются браузером, что тоже верно, а еще и транспортным слоем (инфраструктурным). Что является ключевым аргументом за REST вообще и релевантные коды в частности.


    1. vanxant Автор
      09.04.2019 07:10
      +1

      Вы когда-нибудь пытались сделать клиента к апи, которое на всё подряд кидает 200 ОК? Даже если вы тупо опечатались в запросе, или, самое смешное, сервер падает и не успевает передать ничего, кроме этого самого 200 ОК, которое затем кешируется.


      1. rpiontik
        09.04.2019 07:13

        Вы явно не поняли мой посыл — habr.com/ru/post/440900 То, что вами в статье отражено — верно. Но это не все ключевые аргументы в пользу релевантных кодов ответа как и самого выбора REST.


        1. vanxant Автор
          09.04.2019 07:24

          Ну в этом смысле — да.


          1. keydet
            09.04.2019 16:24

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

            Также, как уже вам заметили другие комментаторы, транспортный слой накладывает свои ошибки, поэтому например «404 эндпоинт на целевом application-сервере не поднят» вы никак не отличите от «404 эндпоинт на целевом application-сервере поднят, но ресурс на нём не найден». Ниже пользователь rpiontik спрашивает, какая разница между этими случаями: разница здесь в совершенно разных сценариях обработки этих ошибок.


            1. vanxant Автор
              09.04.2019 16:36
              -3

              Вот не верю я в количество бизнес-ошибок, которых всегда намного больше.
              Язык С++ как-то обходится 7 типами исключений. В ОС UNIX за 50 лет развития придумали чуть больше 100 кодов ошибок, из которых 2/3 никогда в реальной жизни не используется. Я, конечно, видел иерархии исключений на 1000+ пунктов, только это людям в больших конторах заняться нечем, а оправдывать свою нужность и незаменимость хочется.
              В HTTP из коробки 200 кодов (100 4хх и 100 5хх), которые можно использовать. Вот и используйте. Речь именно о цифровых кодах; для конкретного сообщения у вас есть текст ошибки.
              Изобретатели велосипедов типа вас и GreenLordUA предлагают, получив в подарок автомобиль, выбить лобовое стекло и запрячь лошадьми. Потому что это ж блин надо учиться водить машину.
              Ну если вам не нужен протокол HTTP и вся инфраструктура, которую он предлагает (браузеры, либы, веб-серверы, прокси, балансировщики, PKI наконец) — ну напишите свой бинарный протокол поверх UDP на левом порту, и обрабатывайте ошибки как вам нравится. Делов то. :)


              1. TonyLorencio
                09.04.2019 16:42
                +1

                В HTTP из коробки 200 кодов (100 4хх и 100 5хх), которые можно использовать

                Неправда, из коробки в стандартном HTTP кодов почти на порядок значительно меньше.


              1. xitt
                09.04.2019 17:48

                Весело. Почему вы думаете что все ошибки должны иметь сервер-сайд текст сообщения, без кода? И что, чтобы подсветить конкретное поле на форме, я должен парсить этот текст?


                1. vanxant Автор
                  09.04.2019 17:52

                  Ну если у вас сервер знает, какое конкретно поле должен подсветить клиент, у вас всё ещё есть кастомные заголовки. Ну или можете отдавать на ошибки json.


                  1. xitt
                    09.04.2019 21:12

                    ясно, спасибо.


              1. keydet
                09.04.2019 18:27

                Да, вполне может быть, что вы не сталкивались со сложными бизнес-процессами и Large Enterprise. В SME может быть, да и то мало где. В микросервисной архитектуре на обработке бизнес-ошибок строится огромный кусок оркестрации: перезапустить контейнер, если 404 или пересоздать отсутствующий ресурс, если отдать специальный код а в теле уточнить, что resource not found (это частный микропример, а можно и запустить процесс создания кучи связанных ресурсов, если они связаны в модели и много чего ещё)


                Кодов из коробки совсем мало, нет там и близко двухсот, а те что есть никак не маппятся на бизнес-логику, потому что имеют свое назначение и описание.


      1. zim32
        09.04.2019 10:07

        Как в таком случае отличить 404 Resource not found слоя ппидожения и слоя транспорта. Бизнес требует в первом случае показывать сообщение пользователю чтл ресурс не найден, во втором случае показывать сообщение Что-то пошло не так. Попробуйте позже


        1. rpiontik
          09.04.2019 11:07

          При 404 все пошло так. Просто объекта нет. Это осмысленный ответ сервера. Что-то пошло не так, это 500.


          1. n0wheremany
            09.04.2019 11:24

            Если проще — стой транспорта — нет интернета например в мобильной версии, или накосячили в маршрутизации бэкенда — будет 404, т. к. физически нет такой страницы или она не получена приложением — тут нет сервера, который мог бы отдать 500.


            1. rpiontik
              09.04.2019 12:45

              Понятно. Учту.


          1. mayorovp
            09.04.2019 13:11
            -1

            Вовсе не обязательно осмысленный. Представьте ситуацию: есть сайт 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...


            1. rpiontik
              09.04.2019 14:44

              Это все тот же осмысленный ответ — нет обьекта по этому uri. Не понимаю проблемы. Ну нет там его. Какая разница почему?


              1. LighteR
                09.04.2019 16:51
                +1

                Очень большая разница. На этом может строиться бизнес-логика. И поэтому важно отличать эти два состояния. Представьте что какой-то сервис делает запрос в другой сервис
                GET /api/banned-users/15/
                И на основании ответа решает разрешить ли пользователю логин или нет. Если опираться только на статус 404, то в любая непредвиденная ситуация (сломался роутинг, опечатились в адресе апи в коде, эту апи вообще удалили) будет приводить к серьезным багам в бизнес-логике.


                1. rpiontik
                  09.04.2019 17:20
                  -3

                  Может? Это плохая бизнес-логика, если она может на этом строиться. К каким серьезным багам будет приводить? Поясните. Если нет объекта, его нет. Или у вас вся система будет валиться на том факте, что у вас не оказалось объекта? Ведь это нормально. И если у вас пользователя в БД не окажется — это тоже нормально.

                  А то, что вы пытаетесь донести — о кривезне рук какого-то там админа, так для этого есть интеграционные тесты. И не нужно прикладным уровнем затыкать амбразуру их остуствия и предполагаемую безответсвенность админов.

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

                  Хотите консистентность — RPC. Но автоматом вы теряете все профиты HTTP.


                  1. rpiontik
                    09.04.2019 17:30
                    -4

                    Да хоть обминусуйте :))) Это ж вы с места не двинетесь. Я уже это проходил. Ниче… пережил :)))
                    habr.com/ru/post/440900


                  1. LighteR
                    09.04.2019 17:30
                    +1

                    Это плохая бизнес-логика, если она может на этом строиться.

                    Что значит плохая бизнес-логика? Что вы будете делать, если в бизнес-логике предусмотренны разные сценарии в зависимости от того существует объект или нет?
                    К каким серьезным багам будет приводить? Поясните.

                    Я же специально выше привел конкретный пример. В данном случае произойдет неправильное определение пермишена пользователя, т.е. сможет залогиниться тот, кто на это не имеет право.
                    Да и вообще примеров можно много привести. Любой кейс когда от существования объекта зависит логика. Например, могут удаляться связанные с этим объектом сущности


                    1. rpiontik
                      09.04.2019 17:36
                      -1

                      Ну не сможет. И что? Ответьте на вопрос — и что?! Это будет баг? И какие в ЭТОМ проблемы? Пользователь обратиться в поддержку и «глупого» админа пожурят, а роуты поправят. Проблема то где?
                      Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите. Вообще. Вам кто угодно может ответить что угодно. Это суть HTTP. Это куча серверов между вашим фронтом и бэком. Админ ли у вас глупый или еще кто-то, какой-то вы там код свой придумаете или другой используете это ничего не поменяет. Только усложнит понимание вашей системы теми, кто будет в ней разбираться. И все.
                      Повторюсь еще раз, хотите консистентность это — RPC. Но вы автоматом теряете все профиты от HTTP.


                      1. mayorovp
                        09.04.2019 17:39
                        +1

                        Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите. Вообще.

                        Ну так задача-то была контролировать. Если контроля нет — значит, задача не решена.


                        1. rpiontik
                          09.04.2019 17:49
                          -1

                          Да не было ее. В том то и дело. Попробуйте представить ситуацию, что вы не вольны управлять своим сервером. Т.е. вот он будет отдавать 404 и все тут. И попробуйте проработать свою стратегию работы в этом случае. Я уверен, что у вас выйдет сделать так, чтобы этого вам было достаточно.


                          1. mayorovp
                            09.04.2019 17:58

                            Ну так ответ-то уже был выше. Отдавать ошибку бизнес-логики с кодом 200, а любой ответ 404 рассматривать как ошибку транспорта или конфигурации.


                            1. vanxant Автор
                              09.04.2019 17:59

                              Ещё раз, транспорт легко отдаёт 200 с вообще левыми данными.


                              1. mayorovp
                                09.04.2019 18:03

                                Но не с данными требуемого нужного формата, иначе это уже не просто транспорт, а злоумышленник mitm.


                      1. LighteR
                        09.04.2019 17:48

                        Ну не сможет. И что? Ответьте на вопрос — и что?

                        Вообще-то как раз сможет, хотя не должен. Кейсы могут быть разные от не критичных до очень критичных, которые могут выливаться в реальные финансовые/репутационные потери для бизнеса. Странно, что вы этого не понимаете.
                        Проблема то где?

                        Собственно в потенциальных багах, которые были бы невозможны в случае если 404 не будет являться ожидаемым респонсом.
                        Вы уперлись в свою тантру и не хотите понять простого — вы не контролируете вообще ответ, который получите.

                        Я не понял как это связано с обсуждаемое темой. Мы обсуждаем два варианта:
                        1. Когда мы можем отличить два состояния «ошибка роутинга» и «объект не существует»
                        2. Когда мы не можем отличить эти два состояния
                        2-ой вариант может приводить к багам, пример которых я привел, причем такие баги не всегда просто будет обнаружить. В 1-ом варианте эти баги исключены. К тому же если 404 не считается ожидаемым респонсом, то появление респонсов с таким статусом в логах приводит в срабатыванию алертов и более быстрой реакции на то, что, например, какой-то endpoint исчез


                        1. rpiontik
                          09.04.2019 17:59
                          -4

                          Я очень хорошо понимаю, что такое финансовые и репутационные потери. Так хорошо, что не каждый такой опыт имеет. Без бравады говорю. Именно этот опыт заставил меня относиться к REST как к дождю — это хоршо, когда он есть. Но не часто. И не сильный. И самое главное, я не могу на него повлиять. НО! Я могу взять зонтик или не выходить из дома.

                          1. Когда мы можем отличить два состояния «ошибка роутинга» и «объект не существует

                          Вам не нужно отличать эти две ошибки. В этом нет смысла. В любом случае вы не получите объект. Если уж сильно, сильно, сильно хочится, в респонсе 404 отдавать — Точно, объекта нет. Отвечаю.
                          2-ой вариант может приводить к багам, пример которых я привел,

                          Баги это или нет, но они нарушают работу системы как любой другой. И все, что вы можете сделать — зафиксировать наличие баги. Диагностика это вопрос не пользователя и не софта, а специалистов. И для этого есть логи.
                          В 1-ом варианте эти баги исключены.

                          Как? Баг есть. Админ роут перепутал. У вас 404. Что делать будете?
                          К тому же если 404 не считается ожидаемым респонсом

                          Вообще не учитывается админами. Это крайне распространенный штатный ответ. Разве, что наложены правила на алерты по 404.
                          какой-то endpoint исчез

                          Об этом должны говорить интеграционные тесты. Регулярные. Мониторинг. Но точно не уровень прилоежния. Все, что приложение может сказать пользователю — упс… у нас проблема. Обратитесь в сапорт.


                          1. LighteR
                            09.04.2019 18:11
                            +2

                            Вам не нужно отличать эти две ошибки.

                            Нужно. В одном случае сервис спокойно упадет с 500кой (ну или как-то обработает ошибку, зная что это нештатная ситуация), а в другом будет выполнена бизнес-логика для случая когда объекта не существует.
                            Как? Баг есть. Админ роут перепутал. У вас 404. Что делать будете?

                            500тить, например. Это частичная неработоспособность системы. Плюс к этому на 500ки последует очень быстрая реакция. А в том что предлагаете вы из-за инфраструктурной проблемы каскадно начнет неправильно вести себя бизнес-логика. Получив 500ку я сразу по логам могу установить, что ее причиной было 404 от другого сервиса. А в вашем кейсе сломается логика, но никаких признаков что что-то идет не так не будет
                            Вообще не учитывается админами. Это крайне распространенный штатный ответ. Разве, что наложены правила на алерты по 404.

                            Там где 404 не используют как нормальный респонс, конечно, накладывают такие правила для алертинга
                            Об этом должны говорить интеграционные тесты. Регулярные. Мониторинг.

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


                            1. rpiontik
                              09.04.2019 18:18
                              -3

                              500тить, например. Это частичная неработоспособность системы.

                              Это как? Роут перепутан… ничего до бэка не дошло. Просто 404. Что в этом случае?
                              Там где 404 не используют как нормальный респонс, конечно, накладывают такие правила для алертинга

                              Не в коем разе. Это плохая практика. Ибо вашу поддержку могут просто затроллить. Она сума сойдет разгребая запросы.
                              Роут может сломаться в любой момент, уже после прогона интеграционных тестов.

                              Может. Но это не значит, что он будет сломан постянно. Через установленный промежуток времени, мониторинг покажет ошибку. Она будет исправлена. Пользователи продолжат работу.
                              404 мониторингами не отрабатывается. Только для конкретных роутов, где скажем, на любой запрос, должен даваться ответ. Да думаю и там не будет. Обрабатываются 5хх ошибки.
                              Вообще 4хх не должно обрабатывать мониторингом. Только, повторюсь, в виде исключений.


                              1. LighteR
                                09.04.2019 18:26

                                Это как? Роут перепутан… ничего до бэка не дошло. Просто 404. Что в этом случае?

                                500тить будет клиент этого endpoint'а, получивший 404. Это нештатная ситуация, в которой клиент не может продолжить работать не получив эти данные.
                                Не в коем разе. Это плохая практика. Ибо вашу поддержку могут просто затроллить. Она сума сойдет разгребая запросы.

                                Вы сейчас имеете в виду запросы, которые приходят снаружи (браузер и т.д.)? Я же имел в виду в первую очередь взаимодействие между внутренними сервисами, где никто никого троллить не станет
                                Может. Но это не значит, что он будет сломан постянно. Через установленный промежуток времени, мониторинг покажет ошибку.

                                В таком случае мониторинг будет носить вероятностный характер. Т.е. нужны еще какие-то сложные правила, что от определенного endpoint'а должен быть какой-то процент не-404 ответов


                                1. rpiontik
                                  09.04.2019 18:45
                                  -1

                                  500тить будет клиент этого endpoint'а, получивший 404.

                                  Это я вообще не понял… какой клиент endpoint? Но даже если это откинуть, то вы просто преборазуете 404 в 500. Зачем-то… хотя суть ошибок разная. И главное, приравнивает (sic!) 404 к 500. Что мешает это сразу воспринимать как то, что вы хотите?
                                  Я же имел в виду в первую очередь взаимодействие между внутренними сервисами, где никто никого троллить не станет

                                  Хух… да я бы про это и слова не сказал. Тут можно делать что хочешь. Это контролируемый периметр. И, я это не считаю полноценным REST. По субъективным причинам.
                                  В таком случае мониторинг будет носить вероятностный характер. Т.е. нужны еще какие-то сложные правила, что от определенного endpoint'а должен быть какой-то процент не-404 ответов

                                  Да не нужно :) Просто не нужно это контролить. НО!!! Если речь идет о внутренних сервисах и клиентах, то да, 404 вполне резонно стоит контролировать. Тогда в этом есть смысл.


                                  1. LighteR
                                    09.04.2019 19:02

                                    Это я вообще не понял… какой клиент endpoint?

                                    en.wikipedia.org/wiki/Web_API#Endpoints
                                    Если речь идет о внутренних сервисах и клиентах, то да, 404 вполне резонно стоит контролировать. Тогда в этом есть смысл.

                                    Наконец-то мы поняли друг друга


                                  1. LighteR
                                    09.04.2019 19:10

                                    то вы просто преборазуете 404 в 500. Зачем-то… хотя суть ошибок разная. И главное, приравнивает (sic!) 404 к 500

                                    Ну как раз приравнивание ошибок происходит в вашем случае. Недоступность какого-то апи приравнивается к нормальному ответу. Это равносильно приравниванию пустого ответа от субд к отсутствию коннекта к этой субд


                                    1. rpiontik
                                      09.04.2019 19:27
                                      -1

                                      Все, я по кругу не люблю ходить. Когда будете создавать внешний сервис, когда дойдете до того, о чем я вам пишу, плз, если не сложно черканите, если я окажусь не прав ;)


                                      1. LighteR
                                        09.04.2019 19:51
                                        +1

                                        Когда будете создавать внешний сервис, когда дойдете до того, о чем я вам пишу, плз, если не сложно черканите, если я окажусь не прав ;)

                                        Это аргумент в стиле вы тут все неопытные юнцы, один я знаю как правильно делать? Или мне показалось?


                                        1. rpiontik
                                          09.04.2019 20:05
                                          -1

                                          Это факт того, что я могу быть уверенным в том, что я говорю, и даю вам возможность в этом удостовериться. А как это воспринимать, это вопрос вашего профессионального уровня. Я же могу, если у вас возникнут какие-то вопросы, что-то пояснить. Дополнительно.

                                          Один из моих проектов в профиле.


                                          1. LighteR
                                            09.04.2019 20:36

                                            я могу быть уверенным в том, что я говорю

                                            уверенность никак не говорит о вашей правоте или неправоте. У кого-то может уверенность в противоположных вещах.
                                            Один из моих проектов в профиле.

                                            К слову, у вас в профиле никаких проектов нет. По крайней мере, для меня они не видны


                                            1. rpiontik
                                              09.04.2019 20:39
                                              -1

                                              Вы уверенность с самоуверенностью путаете. Слова похожи, но суть у них совсем разная ;)

                                              www.cryptonit.net в профиле. Надеюсь, я ничего не нарушаю.


                            1. Lailore
                              09.04.2019 18:49

                              И еще раз, вам не нужно :) Все логика на существование элемента может только подсказывать валидации ибо при создании последнее слово все равно скажет база, смогла ли она создать или нет. (из-за конкуренции)


                              1. LighteR
                                09.04.2019 18:59

                                Все логика на существование элемента может только подсказывать валидации

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

                                При чем тут создание, если речь шла о получении объекта? И при чем здесь база?


                    1. xitt
                      09.04.2019 17:52

                      =Что значит плохая бизнес-логика? Что вы будете делать, если в бизнес-логике предусмотренны разные сценарии в зависимости от того существует объект или нет?= Использовать 410, как вариант, не?


                      1. LighteR
                        09.04.2019 17:59

                        Ну насчет использования именно 410 я не уверен. Но в любом случае это лучше 404 и позволит эти два состояния отличать. Если конечно есть гарантия, что никто другой между клиентом и сервером не может вернуть такой же статус


                      1. mayorovp
                        09.04.2019 18:02

                        Уже лучше, но не всегда возможно. У 410 семантика «нет и никогда больше не будет», что неприменимо при простом отсутствии в базе и грозит кешированием отрицательного результата.


                        1. xitt
                          09.04.2019 21:17

                          Кеша боятся — в Рест не ходить. По мне кешировать нельзя ничего при обмене данными без особых причин. Но вообще разумное возражение.


                1. xitt
                  09.04.2019 21:21

                  GET /api/banned-users/15/
                  И на основании ответа решает разрешить ли пользователю логин или нет


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


                  1. LighteR
                    09.04.2019 21:29

                    Не очень удачный пример. В этом случае надо вернуть пустую коллекцию или null

                    Ну так я как раз об этом и писал, что в этом кейсе опираться только на статус 404 довольно опасно.


                    1. xitt
                      09.04.2019 21:45

                      Золотое правило — вообще на него не опираться, и не будет проблем. Я не читал диссертацию, но никогда не использую 404 Not Found в качестве «Нет энтити». Это тот самый случай когда нужен и полезен 200 ОК и body — null. Если все палки ломаются вокруг этого, непонятно почему.


                      1. LighteR
                        09.04.2019 22:38
                        +1

                        Золотое правило — вообще на него не опираться, и не будет проблем.

                        согласен
                        Если все палки ломаются вокруг этого, непонятно почему.

                        Вот и мне не понятно


              1. mayorovp
                09.04.2019 17:06

                Потому что нас волнует не наличие объект по заданному url, а наличие нужной записи в базе.


                1. rpiontik
                  09.04.2019 17:24
                  +1

                  А с каких пор REST отвечает за наличие записей в БД? На ваш запрос может ответь вообще статика. И вернуть вам тоже самое что из БД. Или кэш.


                  1. mayorovp
                    09.04.2019 17:26
                    -1

                    С тех пор как на основе REST стали строить API.


                    1. rpiontik
                      09.04.2019 17:28

                      Ну так как насчет ответа из кэша? Или статикой на ваш REST API?


                      1. mayorovp
                        09.04.2019 17:34

                        Что вы понимаете под "татикой на ваш REST API"? Вот есть сервер приложений, он отвечает на запросы к API. Откуда возьмётся статика?


                        1. rpiontik
                          09.04.2019 17:47

                          Очень простой факт, что любой высоконагруженный проект в итоге, приходит к тому, что часть REST обрабатывается самим web-server. Например он кэширует запросы к бэку и и скалдывает к себе, а затем, если кэш актуальный, отдает статику. И к БД не происходит обращений. Также микросервисы могут интервально генерировать статику из БД, а сам REST будет обрабатывать webserver. И что вы предполагаете тут отдавать?

                          Дело в том, что вы ограничиваетесь простейшими кейсами. И находите в них проблемы. Хотя их нет. Возьмите и помониторьте серьезные сайты на предмет работы REST. Сделайте выводы оттуда, а не из дебатов на хабре.


                          1. mayorovp
                            09.04.2019 17:55

                            Если сделать то, что вы написали, как полагается, то статика будет отражать содержимое БД. А значит, исходя из результатов запроса всё еще можно будет судить о наличии объекта в БД.

                            А если сделать абы как, то, собственно, и всплывут те самые проблемы о которых и писали выше, когда ответ сервера ничего не означает, а ошибка бизнес-логики неотличима от транспортной.


                            1. rpiontik
                              09.04.2019 18:08

                              Я пытаюсь вам донести, что REST в любой момент масштабируется. В этом его профиты. И в инфраструктуру вставляются новые сущности. Хотя все остальные даже не замечают этого. Это очень и очень круто.
                              И делать выводы о том, что чего-то нет в БД неверно. Нужно именно судить о том, что по этому урлу нет объекта. А где его нет… в кэше, в БД или еще где-то это вопрос вторичный. Вы получите 404. И вместо него просто не нужно ничего придумывать. Достаточно это принять и с этим работать.
                              Если хотите удостовериться в том, что ответ от бэка, то вводится новая сущность в загловки — fingerprint. По которой можно заключить о том, кто эмитировал этот ответ. Но чаще всего, это вредно отдавать фронту. И эта фича используется во внутренней инфраструктуре для идентификации в логах.


                              1. mayorovp
                                09.04.2019 19:08
                                +1

                                Но что делать, если задача в том и заключалась, чтобы определить есть ли что-то в БД?


                                1. rpiontik
                                  09.04.2019 19:34
                                  +1

                                  Так :))) Тоже пошли по кругу. REST не БД, даже если ответ берется из БД. Это лишь частный случай реализации в определенный интервал времени. Давайте закончим, т.к. я вижу желание во что бы то не стало остаться при своем мнении. Я «биться» за «правду» внутри вас не горю желанием. Мне достаточно, что я точно знаю как это работает и почему. Это доказывается моими проектами. Один из них можете посмотреть в профиле.


                                  1. LighteR
                                    09.04.2019 20:46
                                    +1

                                    REST не БД, даже если ответ берется из БД

                                    Да собственно не важно БД или не БД. У нас есть ресурс с определенным URI и мы хотим проверить существование этого ресурса. Если мы получаем ложноотрицательный ответ (в случае когда 404 вернулся из-за недоступности сервиса) о существовании этого ресурса, то это неконсистентное состояние системы. В каких-то случаях такая неконсистентность допустима, в каких-то — нет


                                    1. rpiontik
                                      10.04.2019 08:15

                                      в случае когда 404 вернулся из-за недоступности сервиса

                                      Недоступность сервиса — 503.


                                      1. mayorovp
                                        10.04.2019 09:12

                                        Я выше уже приводил конфигурацию, при которой обращение к остановленному сервису приведет к коду 404.


                                        1. rpiontik
                                          10.04.2019 11:16

                                          Не приведет. Это ошибка в инфраструктуре, точнее тотальные косяки в инфраструктуре и полный бардак в ней же могут привести к 404 при недоступности сервиса. Т.е. мы создаем себе трудности и героически их преодолеваем…


                                          1. mayorovp
                                            10.04.2019 11:22

                                            Разумеется, это ошибка в инфраструктуре. Но ошибка легко допускаемая и способная долгое время оставаться незамеченной.


                                            1. rpiontik
                                              10.04.2019 12:00

                                              Нелегко. Преобразовать 503 в 404… это нужно ОЧЕНЬ сильно постараться. ОЧЕНЬ. Админы и DevOps поправьте меня плз ;)


                                              1. mayorovp
                                                10.04.2019 13:19

                                                Я же приводил пример как это сделать. Ничего сложного, достаточно повесить под виндой статический сайт и его api на один порт. И ошибка будет проявляться только при отключенном api, но включенном сайте — то есть, как правило, в короткие промежутки рестарта службы.


                                                1. rpiontik
                                                  10.04.2019 13:37

                                                  Да что ж такое :))) Ну «не может быть» такого примера, понимаете? Это нужно ПОСТАРАТЬСЯ так сделать. Вы знаете как конфигурируется nginx для работы с бэком? Думаю нет, отсюда ВАША проблема. Он может либо проксировать на какой-то другой сервер, либо отдавать по правилам конфигурации. И в конфигурации ЧЕТКО приписываются роуты по которым будет жить бэк. И если там бэк не отвечает… это 503 или 502, не помню точно. Но уж точно не 404. Понимаете?
                                                  А если он проксирует, то другой сервер живет по той же системе. И тоже отдаст 5хх.
                                                  А уж если вы запроксируете напрямую в какой-то самопальный бэк… то… отгадайте, что ответит nginx если по этому порту никто не ответит?
                                                  Ну почему, я должен вам доносить очевидные вещи? Я уже и так вам «намекал», и так… теперь мне вменят переход на личсноти и т.п. :)))


                                                  1. LighteR
                                                    10.04.2019 13:53

                                                    Ну не может быть такого примера, понимаете?

                                                    навскидку:
                                                    • Service Discovery стал вести не в тот контейнер
                                                    • Ошибка конфигурации API Gateway
                                                    • В коде сервиса сделали ошибку, что привело к изменению (или вообще удалению) url'а, и соответсвенно запрос на него всегда возвращает 404
                                                    • Ошибка в роутах с regexp'ами типа user/[0-9]{2})/ user/42/ работает нормально user/666/ всегда возвращает 404


                                                    1. rpiontik
                                                      10.04.2019 14:00
                                                      -1

                                                      Т.е. с nginx тема не прокатила, теперь будем как-то по другому искать варики… возмите плз и сами найдите ответы почему эти сценарии надуманы. Правда — устал. Извините.


                                                      1. LighteR
                                                        10.04.2019 14:15

                                                        Т.е. с nginx тема не прокатила

                                                        Я вообще ничего про nginx не писал.
                                                        возмите плз и сами найдите ответы почему эти сценарии надуманы

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


                                                        1. rpiontik
                                                          10.04.2019 14:54
                                                          -1

                                                          Yep. Так и отвечаю. Уже 30 раз. Ровно так.
                                                          А вы так:

                                                          конфигурации API

                                                          и
                                                          Все ваши аргументы против


                                                          Так и общаемся…


                                                  1. mayorovp
                                                    10.04.2019 13:54
                                                    -1

                                                    Во-первых, вы пропустили вторую часть, которая про IIS и HTTP.SYS

                                                    Во-вторых, в том же nginx можно попросту забыть прописать location к api, или опечататься и сделать location для какого-нибудь apo. Если при этом для всего сайта целиком указана директива root — то результатом как раз и станет 404 в ответ на любой запрос к api.


                                                    1. rpiontik
                                                      10.04.2019 14:04
                                                      -1

                                                      Ответ сервера будет всегда релевантен событию. Более подробно можно почитать в мануалах по его конфигурированию. И IIS и nginx и apache.


                                      1. LighteR
                                        10.04.2019 10:13

                                        Недоступность сервиса — 503.

                                        Ну не надо передергивать. Вы прекрасно поняли что я имел в виду: например, сломали роутинг. По существу можете что-нибудь ответить? Что делать с неконсистентностью в случае когда она неприемлема?


                                        1. rpiontik
                                          10.04.2019 11:17

                                          Я вам это уже не единожды говорил — RPC. Причем именно он а не очередные велики. Т.е. с манифестом методов, параметров и биндом методов на uri.


                                          1. LighteR
                                            10.04.2019 11:20

                                            Если отказаться от идеи использовать 404 как нормальный ответ от api это же решит проблему, которую мы обсуждаем?


                                            1. rpiontik
                                              10.04.2019 12:05

                                              Ну нет же. Прочитайте пожалуйста мою статью habr.com/ru/post/440900 в части «Коды ответа http в REST» там я популярно изложил все стадии «принятие» REST. Ну я же старался…


                                              1. LighteR
                                                10.04.2019 13:21

                                                Прочитайте пожалуйста мою статью habr.com/ru/post/440900 в части «Коды ответа http в REST» там я популярно изложил все стадии «принятие» REST.

                                                Прочитал. 404 упоминается ровно один раз:
                                                Т.е. если не найден объект по запросу, это 404

                                                Утверждение без какой-либо аргументации.
                                                Если говорить про статью, я согласен, например, с доводами про использование http 500 и что вообще хорошо бы уже по статусу отличать состояние, когда все прошло успешно (2xx) от неуспешного (>=400, что с этим дальше делать клиенту уже другой вопрос). Но у меня есть претензия конкретно к использованию 404.


                                                1. rpiontik
                                                  10.04.2019 13:49

                                                  Так просто экстраполируйте это на 404. Не вижу разницы. Что ж мне для каждого кода статью делать?


                            1. zim32
                              09.04.2019 21:07

                              Дабы прервать этот срач, предлагаю со стороны приложения добавлять какой-то заголовок по типу X-Application: true. Тогда можно проверить что ответ именно от приложения. Если только всякие прокси не урежут заголовок


                              1. LighteR
                                09.04.2019 21:15
                                +2

                                Ну вот мы, ожидаемо, и пришли к тому, что одних http-статусов не достаточно и нужно отправлять дополнительные данные в body/headers и в клиенте опираться на них


                              1. zim32
                                09.04.2019 22:30

                                А еще лучше какой-то хеш подпись, тогда можно отфильтровать еще и подделки


                                1. rpiontik
                                  10.04.2019 08:05

                                  Чаще всего проблема в том, что люди не умеют вчитываться, т.е. читают но не видят. И этот тред прекрасное тому подтверждение. Он начился с того, что сам автор расценил мой пост как хайперский по отношению к статье, а он как раз, напротив, развивает его мысль. Теперь заканчивается тоже эпично. Буквально 2 поста до этого я пишу

                                  Если хотите удостовериться в том, что ответ от бэка, то вводится новая сущность в загловки — fingerprint. По которой можно заключить о том, кто эмитировал этот ответ. Но чаще всего, это вредно отдавать фронту. И эта фича используется во внутренней инфраструктуре для идентификации в логах.


                                  И тут бац
                                  Дабы прервать этот срач, предлагаю со стороны приложения добавлять какой-то заголовок по типу X-Application: true.

                                  потом бац
                                  А еще лучше какой-то хеш подпись, тогда можно отфильтровать еще и подделки


                                  Вопрос… с очевидным ответом — смысл тут распинаться?


            1. werevolff
              10.04.2019 04:42
              -1

              А если админ ошибётся, и вы будете получать сутки 204 ответ?
              Что за чушь вы несёте?! Пойти и сломать ваш сервер так, чтобы он возвращал чепуху — ерунда для админа. Но есть, короче, метод. На крутых REST проектах админам резиночкой мудики перетягивают, и они перестают генерировать нестандартные ошибки.


              З.Ы. если админ ошибётся, вы будете, скорее-всего, получать 502, 503 или Timeout Error (забыл его код). Если ваш админ так не умеет — срочно бегите за резиночкой!


        1. vanxant Автор
          09.04.2019 14:02

          Слой транспорта может швыряться самыми разными ошибками, не только 404. Тут вам и 408, и 451, и 502/503, и что угодно. Чего он не может — так это слать куки и CSRF токены. Так что смотреть надо на них.


        1. werevolff
          10.04.2019 04:34
          +1

          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 код возвращает роутер, проверяющий существование ресурса.


          1. Regis
            10.04.2019 05:42
            +1

            /api/path/video/1234 — это конкретный запрос ресурса — ролика с идентификатором. Если отсутствует любой узел, включая 1234, возвращается 404.
            Как вы отличите ситуацию «видео с таким номером нет» и ситуацию «у нас еще приложение не до конца задеплоилось, так что роутинг не работает и веб-сервер отвечает стандартным 404»?


            1. werevolff
              10.04.2019 11:02

              Как вы отличите ситуацию «видео с таким номером нет» и ситуацию «у нас еще приложение не до конца задеплоилось

              503 service unavailable
              Ставится на деплое и не сношается моск.


      1. mayorovp
        09.04.2019 13:00
        +1

        Такое api называется JSON-RPC. Не вижу проблем сделать к нему клиента, если не забывать смотреть на тело ответа (а если забывать — не понимаю зачем вообще нужен клиент и api).


      1. GreenLordUA
        09.04.2019 13:15
        +1

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

        Абсолютно реальный, не надуманный кейс, с которым лично я сталкивался три раза: два раза на мобильных клиентах германии, находящихся в роуминге (один из них Т-мобайл, второй уже не помню), один раз — на итальнском мобильном операторе (вне роуминга, в родной сети).

        Выглядит следующим образом — все, что попадает на мобильный телефон — проходит через прокси мобильного оператора. Прокси оператора, видя стандартные коды ошибок (400, 401, 403, 404 (!), 500) режет ответ вашего сервера. Он отрезает весь ответ после заголовков (с целью экономии трафика?). Поэтому любые детали об ошибке, которые вы передаете в теле ответа — просто теряются.

        А вот 200й ответ не режется никогда (конечно, субьективное мнение, не более). Пережимается (удаляются переносы строк, не значащие символы) — видел. Но это не мешает его правильно интерпретировать клиенту.

        Поэтому в случае, если вам нужно быть максимально совместимым — я бы как раз рекомендовал обратное — отдавать ошибки столь ненавистным вам способом.


        1. vanxant Автор
          09.04.2019 13:17

          https спасёт отца русской демократии от вмешательства опсосов и прочих барабашек.


          1. GreenLordUA
            09.04.2019 14:11

            подобный прокси это хрестоматийный пример man-in-the-middle: вы можете гарантировать, что они не подменят сертификат и не сделают то же самое?


            1. vanxant Автор
              09.04.2019 14:21

              Для подмены сертификата провайдером нужно решение на государственном уровне, и всё равно есть способы защиты.


              1. GreenLordUA
                09.04.2019 14:31

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

                а при этом решение с ответами 200й будет работать — в любых условиях. https/https, сниферы/прокси, реальные сертификаты/подменные.

                просто будет работать. всегда и везде.


                1. vanxant Автор
                  09.04.2019 14:33

                  Да не будет оно работать всегда и везде. 200 ОК с сообщением об ошибке моментально закешируется и всё, алес капут.


                  1. GreenLordUA
                    09.04.2019 14:59

                    плохой довод, играет и против вас, и против меня. если кеширование на прокси настроенно настолько не правильно, что кеширует разные по длине/хидерам 200е ответы — оно точно так же, может кеширвоать и не 200е коды.

                    или же — кешировать ваши правильные данные (без ошибок) для вашего варианта — вне зависимсоти от того, как вы кодируете на уровне протокола сами ошибки.

                    плюс — приведу ваш же довод — ssl.

                    ну а в варианте — прокси, с подменой сертификатов. да и не правильно настроенным кешированием — апи просто не будут корректно работать.


                    1. vanxant Автор
                      09.04.2019 15:13

                      Да при чём тут прокси, сам браузер 200 ответы и закеширует.
                      Повлиять на политику кеширования браузерами вы не можете, придётся плясать по их правилам. А для них 2хх — это коды годных результатов.


                      1. GreenLordUA
                        09.04.2019 15:46
                        +2

                        почему это вдруг наш клиент — браузер?

                        вы, пардон, описываете принципы построения REST JSON API, а не «особенности построения REST JSON API для браузеров».

                        там может быть и мобильный клиент, и интернет вещей как клиент — да кто угодно.

                        да и с кешированием 200х ответов по API браузером вы, мягко говоря — лукавите.


                      1. mayorovp
                        09.04.2019 17:08

                        Повлиять на политику кеширования браузерами вы не можете

                        А Cache-Control на что?


                        1. vanxant Автор
                          09.04.2019 17:41

                          Если ошибка выброшена промежуточными слоями (прокси, балансировщиками, самим браузером), то cache-control там не будет, или будет такой что лучше бы его не было.


                          1. mayorovp
                            09.04.2019 17:44

                            А разве речь тут шла об ошибках промежуточных слоёв? Мне почему-то казалось что тут обсуждается с каким кодом сообщать об ошибке бизнес-логики.


                            И да, ошибки промежуточных слоёв разве с кодом 200 идут?


                            1. vanxant Автор
                              09.04.2019 17:56

                              Специально зашёл на ресурс имени роскомпозора — 451 там и не пахнет, всё 200 и пара 302 зачем-то.


        1. vdonich
          10.04.2019 00:32
          +1

          А кто, собственно, мешает провайдеру в таком случае подменять ответ сервера даже со статусом 200? Только совесть.

          По науке автор прав — не надо прогибаться, есть стандарт.
          На практике, конечно, выйдет немного по-другому — если есть нужда работать через такой прокси в такой позе — будет работать, или у тебя, или у следующего разработчика.
          Значит, сервер должен иметь lame duck mode специально для такого случая, который активируется или дополнительным хедером, или параметром, или протоколом, или просто по IP.

          Чего совершенно нельзя делать, так это брать нестандартные решения и делать из стандартными — суть стандартов в том, что они более-менее универсальны.

          С тем же успехом может прийти какой-нибудь камрад с монгольского прокси и выдвинуть несовместимые требования — мол, у нас HTTP 200 считаются трафиком, а остальные нет, давайте 204 вместо 200.


      1. Tangeman
        11.04.2019 01:44

        Вы когда-нибудь пытались сделать клиента к апи, которое на всё подряд кидает 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 — тогда да, в самый раз. Увы, в реальности такое бывает редко.


        1. vanxant Автор
          11.04.2019 01:59

          Batch processing в http есть, не скажу что прям просто реализуется, но есть.
          Вопрос в том, что отказываясь от нормальной работы с HTTP, вы также отказываетесь от большей части инфраструктуры (балансировщики, кэши, прокси, мониторинг, etc). На всё можно свои велосипеды наколхозить, только зачем? Тогда уж лучше взять какой-нибудь стандартный RPC типа SOAP, где есть своя инфраструктура.


          1. Tangeman
            11.04.2019 03:06

            Batch processing в HTTP нет, есть только pipelining, а это совсем не то же самое (хотя в ограниченных случаях и применимо). В HTTP вообще почти ничего нет — это просто транспорт, не более того. Да, получивший широкое распостранение и вне web, передающий уже давно не только HTML, обросший прокси, кэшами и LB, но всего лишь транспорт.

            Честно говоря, я не вижу каким образом использование 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).


            1. vanxant Автор
              11.04.2019 06:05

              Вот только зачем это всё для отображения камментов каким-нибудь vue.js?


              1. LighteR
                11.04.2019 09:00
                +1

                Область применения HTTP API не ограничивается только браузерным js'ом


  1. alexs0ff
    09.04.2019 08:22

    Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать

    Кто привык к этому, добро пожаловать в GraphQL, там нет заморочек с кодами возврата, адресами ресурсов (точка входа вообще одна). Все очень просто устроено.


  1. kether
    09.04.2019 08:31

    418 I'm a Teapot

    Существует только в первоапрельском RFC 2324, то есть не включен ни в один стандарт. И даже по вашей логике его отличия от стандартного 400 Bad Request довольно неопределённые.


    1. vlreshet
      09.04.2019 09:37

      Вот вы смеётесь, а я отдаю 418 в своём приложении. В месте которое является пасхалкой, и никто кроме девелоперов с devtools туда не полезет :)


    1. vanxant Автор
      09.04.2019 13:22

      Именно, что 418 первоапрельский RFC, и поэтому не будет выкинут ни одним промежуточным софтом.
      В дев/staging окружении 418 это железобетонный баг клиента, можно прям автоматом баг заводить в трекере.
      На проде 418 означает, что клиент у нас хакер, спамер, сканер или фаззер, и его можно спокойно отдавать fail2ban или аналогу.


      1. kether
        09.04.2019 13:34

        418 первоапрельский RFC, и поэтому не будет выкинут ни одним промежуточным софтом
        Не понял причинно-следственной связи: почему не будет?

        RFC 2324 Status: INFORMATIONAL

        Strict-реализация HTTP совершенно спокойно может отбрасывать коды вне стандарта.
        Вы же не будете утверждать, что различный софт и железяки должны поддерживать вот это всё.


  1. samizdam
    09.04.2019 08:31

    > Идемпотентный запрос, т.е. повторный DELETE с таким же адресом не приводит к ошибке 404.

    Можете аргументировать?


    1. Carduelis
      09.04.2019 11:01

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


    1. worldxaker
      09.04.2019 11:39

      1. LighteR
        10.04.2019 11:16

        Идемпотентность в контексте http не означает что response status должен быть одинаковый, а говорит только о том, что при повторных запросах не должно появлять дополнительных сайд-эффектов на стороне сервера.


    1. EvgeniiR
      10.04.2019 12:00

      > Идемпотентный запрос, т.е. повторный DELETE с таким же адресом не приводит к ошибке 404.

      Можете аргументировать?

      Вы отправляете DELETE запрос на ресурс с ID 100.
      Ресурс удалён? Удалён. Возвращаете информацию о том то удаление прошло успешно, вот и всё )
      Можно ещё запариться с ключами идемпотентности. Хорошая статья была совсем недавно — habr.com/ru/company/yandex/blog/442762


  1. TonyLorencio
    09.04.2019 09:21

    Всё, описанное здесь, конечно хорошо и здорово. Наверное, стоит прислушаться к полезным пунктам этого поста, но не нужно путать теплое с мягким.


    REST API это всего лишь два слова, не несущие никаких четких стандартов. Никто не обязывает использовать HTTP или не использовать "велосипеды со всякими {error: "message","result":...}".


    Соответственно, все пункты, касаемые того, что должны делать сервер и клиент (заголовки Accept, тип text/plain при ошибке, использование множества кода ошибок вместо 200/400/500, в крайнем случае — 404, причем зачастую люди трактуют их неверно, привет коду 418), это не стандарт, а пожелание автора, пусть хорошо понятное и в целом для многих справедливое. Нужно помнить об этом.


  1. dopusteam
    09.04.2019 09:32

    Стандарт HTTP это стандарт. Его несоблюдение вредно для кармы и ведёт к постоянным проблемам с безопасностью, кэшированием и прочими «закидонами» браузеров, которые совсем не закидоны, а просто следование стандарту.

    Непонятно, каким образом это относится к REST API. Это относится к любому неправильно спроектированному API, нет разве?

    Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать

    Не могли бы раскрыть свою мысль?

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

    Тоже интересно узнать подробности, о чём конкретно идёт речь?

    Поддержка автоматизированного интеграционного тестирования. Когда сервер на любые запросы отдаёт 200 ОК — ну, это такое себе развлечение.

    Ну так надо смотреть тело, разве нет?

    Вообще, натягивание статусов протокола на приложение, как по мне, не самое лучше решение.
    Если я отправляю запрос на получение сущности, а возвращается 404, что это? Страница не найдена вообще или сущность не найдена? Как это различать?

    Или, например, есть запрос на получение данных, который принимает большое количество аргументов. Например, сложный фильтр, который может быть совершенно произвольной формы и не ограничен по размеру. Что в этом случае делать?

    Т.е. в целом, я не против REST API, но это всего лишь соглашение, не всегда удобное, не стоит его навязывать всем.


  1. n0wheremany
    09.04.2019 09:41

    Велосипеды со всякими {error: «message»,«result»:...} невозможно нормально тестировать и отлаживать

    Почему вы считаете это велосипедом? Microsoft API

    Если клиенту нужно отобразить ошибку? Даже тривиально — результат валидации?


  1. Quilin
    09.04.2019 09:54

    Оборачивать результат списка ресурсов (GET /entities) необходимо для закрытия уязвимости JS Array, которая может привести к утечке данных.
    Побочным бонусом от этого оборачивания становится возможность докинуть информацию о пагинации не в заголовки.


    1. Carduelis
      09.04.2019 12:21

      А что-за уязвимость, ведущая к утечке памяти? Не наследие ли это всяких IE6, Netscape?
      Про побочный бонус — категорически согласен.


      1. Quilin
        09.04.2019 17:50

        Не памяти, а данных. Когда сторонний клиент (например, расширение браузера) может получить доступ к тому, что ваш API передал через свой контракт ответа.


      1. ecmaeology
        09.04.2019 19:15

        Уязвимость 10+ летней давности с перезаписыванием Array


    1. vanxant Автор
      09.04.2019 13:18

      Побочным бонусом от этого оборачивания становится возможность докинуть информацию о пагинации не в заголовки


      С этим соглашусь. Добавил в статью.


  1. Perlovich
    09.04.2019 10:53

    Если вы используете защиту от CSRF (а лучше бы вам её использовать), то удобнее передавать CSRF-токен в отдельном заголовке (типа X-CSRF-Token) для всех запросов, а не внедрять вручную в каждый запрос. Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.


    Это не верно.

    Во-первых, суть CSRF атаки не состоит в том, чтобы украсть кукисы. Она состоит в том, что приложение злоумышленника шлет запросы от лица пользователей в другое веб-приложение.

    Во-вторых, кукисы с CSRF-токенами нельзя украсть, если в браузере отсутствуют уязвимости. Вся суть защиты от CSRF-атак строится на том, что кроме оригинального сайта ни у кого больше нет доступа прочитать содержание кукисов.

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

    Вся суть защиты состоит в том, что клиент отправляет одновременно токен и в хеадере, и в кукисах. И этот токен должен совпадать.


    1. mayorovp
      09.04.2019 12:51

      Уточнение: только не в хеадере, а в теле запроса. Если же отправлять в заголовках HTTP — то кукисы уже не нужны, поскольку сам факт наличия постороннего заголовка означает, что этот запрос сформирован тем, кому разрешено добавлять заголовки в запросы.


  1. Carduelis
    09.04.2019 10:56

    > Придумать реальный use-case, когда реакция клиента будет различаться в зависимости от 404 или 410, довольно сложно.

    Самый простой пример — проверка никнеймов или страниц, связанных с ними. И если мы заходим на несуществующий никнейм — мы видим 404 и кнопку «Занять никнейм», а если кто-то уже когда-то занял его, а затем удалил свою страницу (прямо как в ВК), то кнопка «Занять никнейм» уже не появляется.


  1. jetcar
    09.04.2019 11:27
    -1

    а чем RPC плох? по мне так использование http кодов и методов нафиг не нужно ни браузеру ни коду, это выглядит как хаки, пытаться впихнуть в стандарт то что там не должно быть, с урлами такая же хрень, ведь нужно только название метода которое должно быть понятно и всё можно через пост посылать, может конечно я где-то не прав, но ведь по идее нужно удалённое выполнение функции которая чтото вернёт или не вернёт ответ, а хттп всеголишь способ коммуникации


    1. Carduelis
      09.04.2019 12:19

      > использование http кодов и методов нафиг не нужно ни браузеру ни коду
      Браузеры обрабатывают http-коды по-разному. Множество инструментов по-умолчанию настроены на стандарт и отвечают соответственно.

      Коду тоже нужно. Когда вы используете самый стандартный `fetch API` в браузере, он учитывает коды и выдает TypeError в случае 4xx/5xx

      Метод OPTIONS, тоже как часть стандарта, определяет возможности сервера и участвует в CORS.

      P.S. Junior'ы, ключевое слово сообщения сверху — «по мне».


      1. jetcar
        09.04.2019 13:32

        rest api правила придуманы не просто так, а для упрощения интеграции, это нужно когда нет или не хочется писать хорошую документацию, если мы включаем всякие автогенераторы документации и генераторы кода, то смысл в этом всём пропадает, становиться можно писать апи любой сложности и вообще не думать как оно по сетке посылается, как в общем никто и не думает при вызове метода сторонней библиотеки из кода, становиться не важно где код исполняется, на этой же машине в этом же процессе или где-то удалённо, это всеголишь ещё одна библиотека которой можно пользоваться, разве это не то что всем в конечном итоге нужно или будем топить за rest api чисто чтоб по феншую было?


  1. a1ia5
    09.04.2019 13:18
    +2

    А почему ошибка передается «без всякого JSON»?


    1. vanxant Автор
      09.04.2019 13:29

      Потому что ошибки будут отдаваться в том числе и на транспортном уровне безо всякого JSON.
      И вас будет двухуровневый код обработки ошибок — сначала смотрим http статус, а затем пытаемся распарсить тело ответа. Вопрос — а что это даст? В чём смысл?


      1. alexs0ff
        09.04.2019 13:42

        сначала смотрим 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


  1. KEKCoGEN
    09.04.2019 13:19
    +1

    В общем полезная статья, но я бы не стал слепо следовать ей во всех случаях.
    * При создании (апдейте) объекта, не вижу ничего плохого чтобы вернуть этот самый созданный\обновленный объект чтобы сократить таким образом коммуникацию и избежать доп. нагрузки когда всегда следом прилетит GET на тот же объект
    * Считаю что очень нужно и правильно при 4хх ошибках возвращать в JSON доп информацию по ошбке. Как то: код\константу ошибки. Сопровождающий текст. Возможно какую то информацию requestUUID и.т.д. Возвращать тело ошибки текстом когда вся остальная система работает с JSON — как минимум странно

    Так же в статье деликатно обошли множество моментов, когда при формировании API сложно создать правильный URI с точки зрения операций над объектами (операции не являющиеся созданием или получением ентити и какие-то промежуточные операции).


    1. mayorovp
      09.04.2019 13:39
      +1

      При создании/обновлении объекта методом PUT ни ответа, ни последующего GET не требуется: ведь новое содержимое объекта должно соответствовать переданному с точностью до форматирования.


      А вот в ответ на запрос PATCH должен возвращаться полученный объект, и об этом написано.


      1. pesh1983
        10.04.2019 09:35

        Это не всегда так. Если, допустим, сервер добавил какие-то поля при создании объекта (например, дата создания или автоматически сгенерированный uuid), то отдельный гет на каждый такой пост выглядит избыточным. Проще и дешевле возвращать объект в ответе целиком.


        1. mayorovp
          10.04.2019 09:48

          Конкретно в описанном варианте чистого 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 для него.


          1. pesh1983
            10.04.2019 09:50

            Я имел ввиду ответ на пост запрос. Про пут или патч речи не было)


            1. mayorovp
              10.04.2019 09:58

              Но я-то писал про PUT и PATCH…


              1. pesh1983
                10.04.2019 10:00

                Согласен, моя невнимательность) Прошу прощения


  1. mayorovp
    09.04.2019 13:36
    +1

    1. Как отправлять файлы на сервер тут написано, а как скачивать через api — нет. Если следовать всем этим рекомендациям в лоб, то придётся, того и гляди, кодировать файлы в base64 и отправлять на клиент как строку json.


      Мне почему-то кажется, что для части api, работающей с файлами, допустимо использование любых Content-Type. И каждый файл нужно передавать под его "родным" Content-Type, а не как-то ещё.


    2. Неидемпотентность POST может оказаться проблемой. Решением может стать генерация id на стороне клиента и создание сущности методом PUT.


    3. Описание ошибки в text/plain не всегда хорошая идея. Например, если разные ошибки требуется обрабатывать по-разному, а кодов HTTP не хватает. Или если предполагается перевод сообщений об ошибках на стороне клиента.



    1. vanxant Автор
      09.04.2019 14:04

      Видел я и base64 в json:)
      Но вообще статику отдают отдельным nginx веб-сервером, до кода api такие запросы даже не долетают.


      1. mayorovp
        09.04.2019 16:50

        Так это ж не статика. Это те самые данные, которые ранее были загружены на сервер через api.


  1. EvgeniiR
    09.04.2019 16:57
    -1

    Автор, то что вы описываете в статье называется Remote Procedure Call (RPC), а не REST.
    У вас не происходит управления состоянием клиента со стороны сервера, вы просто предоставляете эндпоинты для вызова процедур. Вот даже возмущение Роя Филдинга по этому поводу, хотя вы видимо, о нем, даже не слышали. Уточню, Рой Филдинг — это человек который ввел понятие 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


    1. mayorovp
      09.04.2019 17:13

      Если речь идёт о чём-то кроме гипертекста, а клиент является чем-то кроме браузера, то клиент в любом случае должен понимать схему данных, хотя бы минимально.


      А если клиент работает без участия пользователя — то без строгой схемы данных ну никак не обойтись.


      1. EvgeniiR
        09.04.2019 17:20
        -1

        Если речь идёт о чём-то кроме гипертекста, а клиент является чем-то кроме браузера, то клиент в любом случае должен понимать схему данных, хотя бы минимально.

        Без проблем, только REST-ом это не называйте. Я упомянул что реализовывать свой кастомный REST клиент вместо существующего(в виде браузера), весьма затратно.

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


        1. mayorovp
          09.04.2019 17:26

          Но в таком случае зачем вообще придуман термин REST? Для интерфейса «как в браузере» уже есть название — «браузер». И для протокола HTTP тоже есть название — HTTP.


          1. EvgeniiR
            09.04.2019 17:34

            Но в таком случае зачем вообще придуман термин REST? Для интерфейса «как в браузере» уже есть название — «браузер». И для протокола HTTP тоже есть название — HTTP.

            REST был придуман 19 лет назад, и тогда он действительно был нужен, и успешно решал некоторые проблемы. А Рой Филдинг является одним из авторов спецификации HTTP, так что их схожесть, точнее то как он удобно ложиться на HTTP вовсе не случайное совпадение )
            Взаимодействие клиентов и серверов в браузере действительно строилось и строится по REST архитектуре, то что это не особо укладывается в архитектуру модных нынче SPA, или не удовлетворяет потребности мобильных разаботчиков, решает проблемы которых их не интересуют не обозначает что нужно обобщать термин REST до уже существующего RPC. Ещё раз повторюсь, RPC — не плохо. RPC это другой стиль решающий другие проблемы, если он решает ваши проблемы, а он решает проблемы большинства, RPC — это хорошо.

            Ну всё же REST он не о протоколе передачи данных, а о взаимодействии компонентов.


            1. mayorovp
              09.04.2019 17:38

              Вы сейчас занимаетесь тем же самым в чём обвиняете оппонентов: вольным расширением терминов. RPC — это, конечно же, хорошо — вот только всё что описано в обсуждаемой статье нормальным RPC не является.


              1. EvgeniiR
                09.04.2019 17:53

                RPC это весьма широкое понятие, в отличии от REST, так что мне интересно услышать чем описанное в статье не «Удаленный вызов процедур», речи о JSON-RPC, я, заметьте, не вёл


    1. vanxant Автор
      09.04.2019 17:35
      -1

      Диссертацию вы может и читали, но ничего в ней не поняли.
      Допустим, у вас бложик. Есть три типа сущностей — записи, комментарии и юзеры. Это три разных ресурса, у которых должно быть три разных URI. Никто не предлагает свалить это всё в одну кучу с одной точкой входа. Клиент, собирающийся получить ресурс, должен знать URI этого ресурса. Это фундаментальная основа всего стиля REST.
      Как оно там на сервере будет обрабатываться — одной точкой входа, которая ловит всё, или отдельным файлом на каждый эндпоинт — это дело сервера, знать о котором клиенту не обязательно.
      Второй столп — полное единообразие на уровнях OSI от транспортного до прикладного. Описанное в статье как раз этому и соответствует. Неважно какой запрос, с одной стороны запихиваем данные в формате JSON, с другой их же и достаём, не задумываясь об их преобразованиях под капотом.
      Никаким RPC здесь и не пахнет.
      Выше прикладного уровня REST не лезет и не должен, это уже слои бизнес-логики. И да, специализированные клиенты должны немного знать о бизнес-логике, на то они и специализированные.


      1. EvgeniiR
        09.04.2019 17:37

        Второй столп — полное единообразие на уровнях OSI от транспортного до прикладного. Описанное в статье как раз этому и соответствует. Неважно какой запрос, с одной стороны запихиваем данные в формате JSON, с другой их же и достаём, не задумываясь об их преобразованиях под капотом.
        Никаким RPC здесь и не пахнет.
        Выше прикладного уровня REST не лезет и не должен

        REST это то что описано в документации Роя Филдинга, а описано там 7 конкретных архитектурных ограничений, не нужно выдумывать что-то своё и коверкать понятия.

        Есть три типа сущностей — записи, комментарии и юзеры. Это три разных ресурса, у которых должно быть три разных URI.

        Это один подпунктик из 7 архитектурных ограничений. Если вы прочитаете мой комментарий то поймете что речь шла о том что клиент про эти URI для начала взаимодействия с вашим сервером знать не должен. Точно так же как мне чтобы читать блог Мартина Фаулера не нужно помнить URI каждой его статьи, так и клиенту для начала взаимодействия с вашем API не нужно знать ничего кроме одной единой точки входа.


        1. vanxant Автор
          09.04.2019 17:40
          -1

          Курите ограничение номер 4 «Единообразие интерфейса», подпункты «Идентификация ресурсов» и следующие два. Пока вы их не осилите, спорить с вами не о чем.


          1. EvgeniiR
            09.04.2019 17:41

            Я обновил комментарий, и уточнил там про идентификацию ресурсов. Ещё раз подчеркну — всё что я выше описал этому принциму никак не противоречит, и каждый ресурс должен иметь URI.


  1. PeterK
    09.04.2019 17:07
    +1

    Неясно, при чем тут REST в заголовке.


    1. EvgeniiR
      09.04.2019 17:49

      Видимо, все кто понял что статья ни о чём просто минуснули прошли мимо, что вобщем то видно по рейтингу статьи. А наш удел остаться тут заминусованными. Печально что на Хабр можно писать любые глупости, и если делать это достаточно убедительно, сотни новичков в поисках информации что такое REST будут натыкаться именно на эту статью…


      1. vanxant Автор
        09.04.2019 17:57

        Уж лучше на эту, чем на тот диссер. 20 лет назад ни JSON, ни даже AJAX ещё не было.


        1. EvgeniiR
          09.04.2019 17:59

          Уж лучше на эту, чем на тот диссер. 20 лет назад ни JSON, ни даже AJAX ещё не было.

          Не делать REST API — хорошо. Никаких проблем.
          Коверкать понятия — никода не хорошо.


          1. vanxant Автор
            09.04.2019 18:01

            Описанное в статье полностью соответствует всем принципам REST.
            Делать кривое АПИ и изобретать велосипеды — плохо.


            1. EvgeniiR
              09.04.2019 18:10

              Описанное в статье полностью соответствует всем принципам REST.

              Как это соответствует как минимум приниципу HATEOAS, если у вас в API не отдаёт гипертекст?
              roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
              Делать кривое АПИ и изобретать велосипеды — плохо.

              Да, плохо.


              1. vanxant Автор
                09.04.2019 19:13
                -1

                Как это соответствует как минимум приниципу HATEOAS

                За 20 лет веб ушёл «немного» вперёд. Кроме того, здесь речь о JSON REST, а не HTML REST.


                1. EvgeniiR
                  09.04.2019 19:27
                  -1

                  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»


                1. EvgeniiR
                  09.04.2019 21:09
                  -1

                  Наговорить глупостей, и тихонько кидать минусы на комментарии и в карму. Мне то без разницы, просто за Хабр обидно. Я ведь тоже когда-то думал что «Ну солидный ресурс же, на Хабре фигни не напишут»


                1. PeterK
                  09.04.2019 21:28

                  REST — это архитектура. Она может быть с JSON, HTML, XML или еще чем-то. Может быть с HTTP, а может и не быть. В статье ни слова про REST нет.


              1. DarkGenius
                10.04.2019 07:58

                В реальном мире большинство REST-сервисов не являются таковыми стопроцентно с академической точки зрения, и это нормально, так как они эффективно решают задачи, ради которых создавались. Такой подход можно назвать "Pragmatic REST". Более того, существует модель зрелости Ричардсона, в которой HATEOAS отмечен как то, к чему следует стремиться, но не то, что обязательно должно быть изначально, чтобы считаться REST (https://martinfowler.com/articles/richardsonMaturityModel.html). Так что в реальном мире выкрики в стиле "Или делайте 100% полностью как описано у Филдинга, или не называйте это REST!" считаю неуместными.


  1. Guitariz
    09.04.2019 17:14
    -1

    Спасибо. Много раз ловил бэкендеров с такими велосипедами, что волосы дыбом. Будет куда отправить просвещаться теперь


  1. kresh
    09.04.2019 18:11
    +1

    А зачем "*/*; q=0.01"?


    1. vanxant Автор
      09.04.2019 19:11

      Потому что вам может прилететь ошибка в неизвестном формате, в том числе от транспортного слоя.


      1. mayorovp
        09.04.2019 19:14

        Но зачем при этом заявлять в запросе о том, что допустим любой формат сообщения об ошибке, в том числе всем транспортным слоям?


        1. vanxant Автор
          09.04.2019 19:15

          Не знаю, спросите у разработчиков браузеров в первую очередь. */* это они вставляют.


  1. Falseclock
    09.04.2019 18:21
    +1

    Очередной велосипед. Зачем? Есть стандарт OData.


    1. vanxant Автор
      09.04.2019 19:15
      -4

      Очередной стандарт MS, 5 версий за 3 года. Спасибо, не всем это интересно.


      1. Falseclock
        10.04.2019 15:39

        Это не стандарт MS, это стандарт от ru.wikipedia.org/wiki/OASIS

        5 версий? Я знаю только 2. И то, последняя содержит не изменения, а дополнения. Да и вообще, от версии к версии стандарт не изменяется, а только дополняется.


  1. nlog
    09.04.2019 18:21
    +1

    Каждый раз, когда я вижу описание принципов REST API, мне хочется узнать: как организовать удаление нескольких объектов по одному действию пользователя? Без использования тела запроса.

    RFC 7231 specifies that a DELETE request may include a body, but that a server may reject the request.


  1. armagast
    09.04.2019 19:51
    +2

    Просто оставлю это здесь: JSON:API


    1. jmdorian
      09.04.2019 20:14

      Из заголовка подумал что о нем и пойдёт речь. Оказалось нет, снова старые песни о главном.


  1. Megadeth77
    09.04.2019 20:02

    Тот случай когда детище переросло своего создателя и живет отдельной от него жизнью. То чем является REST сейчас в общепринятом (насколько это возможно в отсутствии стандарта) представлении к исходной диссертации имеет касательное отношение.


  1. otchgol
    09.04.2019 20:54

    Мне одному кажется что тут RFC в весьма вольном переводе?
    Для знакомых с темой либо избыточно либо поверхностно, а для новичков не прояснилось ничего. Мне видится гораздо более перспективным описание языка декларации сервисов для любых категорий возможных читателей. Может быть я слишком пристрастен, конечно.


  1. FreeBa
    10.04.2019 02:25
    +1

    Суть REST гибкость, отраженная в нескольких строгих правилах. Которые наглухо проигнорированы в статье.

    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. Автор замахнулся на неблагодарное дело…


    1. vanxant Автор
      10.04.2019 03:33

      За не-UTF-8, а особенно за Latin-1, в 2019 надо расстреливать на месте. Если у кого-то в ынтырпрайзе дикое легаси, то это их половые трудности, они всё равно туториалы на хабре не читают. Всем остальным читателям туториала только UTF-8. Там столько граблей и подводных камней с другими кодировками и перекодировками, что нафиг нафиг.

      С остальным даже не знаю с чем спорить. Это долбаный туториал для тех, кто до сих пор первым делом отдаёт 200 ОК и вешает действия на get-запросы. Как бы количество добавлений в закладки намекает, да?


      1. FreeBa
        10.04.2019 07:24

        Т.е. вы не в курсе, что дотнет и ява из коробки поддерживают десяток самых распространенных кодировок (ни в asp.net ни в spring ненадо даже хвостом пошевелить, чтобы предоставить юзеру данные в той кодировке, что он попросит), так и добавление еще одной — дело десятка строк кода?

        Рано вам туториалы писать. Тем более пытаться загнать REST — в искусственные рамки. Они есть, но несколько другие, чем описанные в статье.


        1. vanxant Автор
          10.04.2019 14:00

          Веб, очень мягко скажем, не ограничивается дотнетом и явой. Более того, их суммарная статистическая доля где-то на уровне плинтуса.
          Зато есть куча языков, систем и просто софта, у которого большие проблемы с кодировками, особенно с не-UTF кодировками.
          Также желаю вашему дотнету с явой успехов при работе в однобайтной кодировке с базой, содержащей неанглоязычные символы (а да, мир не только на английском разговаривают). Предположим, вам нужно поискать CITROЁN. Любое перекодирование, даже из UTF-8 в UTF-16, создаст очень большие и весёлые грабли.


          1. FreeBa
            10.04.2019 14:29

            Только вот проблема — REST не ограничен вебом (сюрприз), как веб не ограничен PHP.

            Также желаю вашему дотнету с явой успехов при работе в однобайтной кодировке с базой, содержащей неанглоязычные символы (а да, мир не только на английском разговаривают). Предположим, вам нужно поискать CITROЁN. Любое перекодирование, даже из UTF-8 в UTF-16, создаст очень большие и весёлые грабли.

            Видимо вы очень плохо знакомы с языками общего назначения. Ни для дотнета, ни для явы вобще не составит никакого труда работать с любой базой в любой кодировке, при наличии драйвера, конечно. Более того — разница будет лишь в паре строчек в конфиге и то только в отдельных, тяжелых, случаях.
            Да и вобще, как можно приплести архитектуру(!) взаимодействия к базам данных?


            1. vanxant Автор
              10.04.2019 15:01
              -1

              Вы, видимо, очень плохо знакомы с особенностями юникода в реальной жизни. Как раз таки драйвер может доставить очень больших проблем, потому что его авторы могли иметь весьма расплывчатые представления о коллейшенах и нормализации, а поправить вы мало что можете.
              Впрочем, делайте что хотите — хоть rest без http, хоть перекодирование в Latin-1.


  1. woodhead
    10.04.2019 03:18
    +1

    Подскажите, пожалуйста, как обычно в Rest решается проблема, когда объект нужно отдавать не полностью.
    Например, у сущности User есть поле locked типа boolean. На запрос клиента под логином администратора сервис отдаёт и принимает сущность, обновляя её полностью. При запросе клиентом под учёткой пользователя сервис не может не глядя принимать и обновлять сущность User, так как у пользователя нет прав на редактирование поля locked.


    1. vanxant Автор
      10.04.2019 03:25

      В rest никак не решается. Какие поля кому отдавать, кому разрешать обновлять — это не задача rest, это задача бизнес-логики.


      1. woodhead
        10.04.2019 04:17

        А как вы решаете подобные ситуации? Мне приходят на ум несколько решений, но все кажутся громоздкими в той или иной мере:
        — написать для каждой сущности класс-обёртку;
        — создать кастомный провайдер сущностей для запросов пользователя;
        — создать отдельные URI для пользователя и администратора.


        1. vanxant Автор
          10.04.2019 15:04

          Ну, наиболее универсальный вариант — кастомные представления со своими эндпоинтами.
          Для юзеров будут, например, /users/:id только для чтения и с частью полей, /personal/profile с редактированием, но только своей учётки и тоже не со всеми полями, и /admin/users с полным доступом, но не для всех.


    1. pshhpshh
      10.04.2019 14:54
      -1

      GraphQL


      1. vanxant Автор
        10.04.2019 15:02

        Тоже сам по себе ничего не решает. Проверку и ограничение прав должен делать слой — сюрприз — бизнес-логики. Без этого шаловливый клиент может запросить всё что захочет.


  1. Regis
    10.04.2019 05:51

    Вот вы говорите, что «REST API» — это стандарт. Если так, то где его четкое определение, где спека? Где разбор любых нетривиальных случаев?

    Ах да, вы ссылаетесь на спеку по HTTP. Да вот незадача: та спека совсем не про REST. И не про JSON. И не про то, как правильно завернуть произвольный RPC API в связку HTTP+JSON в качестве транспорта.


  1. 411
    10.04.2019 07:51

    Принципы построения REST JSON API

    При этом описание ошибки, если оно есть, приводится в теле ответа в формате text/plain (без всякого JSON).

    Я не любитель апишек, которые не используют HTTP коды для ошибок, но для JSON API лучше уж 200 и JSON с индикатором ошибки в теле, чем 400 и строка.

    418 I'm a Teapot

    Я думал первое апреля уже прошло, с каких пор 400(и 404 в случае неверных урлов) недостаточно?


  1. Oleg_Yozhik
    10.04.2019 21:44

    Парсить url регулякой — не лучший выбор


  1. iproger
    11.04.2019 08:41

    Вы призываете делать грамотно и хорошо то что по определению является одним большим костылем. REST API имеет милион проблем, многие из которых привели в комментариях. Надо только не нарушать базовые условности и делать как делают все. Возможно, когда придумают идеальный rest 2.0 то появится смысл говорить о «едином» стандарте.

    Я 2 года работал с magento 2 через rest api. Познал всю боль которую только можно было представить. Это с тем учетом что api довольно продумано и выражено в прекрасном swagger. Тысячи полей и мегабайты данных и кучи условий, выборок, коллекций.
    Довольно забавно потом читать учения как надо сделать 2+2 и отправить очередное name в /product.