Введение в REST API — RESTful веб-сервисы
Эта статья начинает серию постов о разработке REST API:
Intro to RESTful Web Services
REST означает REpresentational State Transfer (Википедия: «передача состояния представления»). Это популярный архитектурный подход для создания API в современном мире.
Вы изучите:
Что такое REST?
REST расшифровывается как REpresentational State Transfer. Это был термин, первоначально введен Роем Филдингом (Roy Fielding), который также был одним из создателей протокола HTTP. Отличительной особенностью сервисов REST является то, что они позволяют наилучшим образом использовать протокол HTTP. Теперь давайте кратко рассмотрим HTTP.
Краткий обзор HTTP
Давайте сначала откроем браузер и зайдем на веб-страницу:
А затем щелкните на одной из страниц результатов:
Далее мы можем нажать на ссылку на странице, на которой мы оказались:
И перейти на другую страницу:
Вот как мы обычно просматриваем веб страницы.
Когда мы просматриваем страницы в Интернете, за кулисами происходит много вещей. Ниже приведено упрощенное представление о том, что происходит между браузером и серверами, работающими на посещаемых веб-сайтах:
Протокол HTTP
Когда вы вводите в браузере URL-адрес, например www.google.com, на сервер отправляется запрос на веб-сайт, идентифицированный URL-адресом.
Затем этот сервер формирует и выдает ответ. Важным является формат этих запросов и ответов. Эти форматы определяются протоколом HTTP — Hyper Text Transfer Protocol.
Когда вы набираете URL в браузере, он отправляет запрос GET на указанный сервер. Затем сервер отвечает HTTP-ответом, который содержит данные в формате HTML — Hyper Text Markup Language. Затем браузер получает этот HTML-код и отображает его на экране.
Допустим, вы заполняете форму, присутствующую на веб-странице, со списком элементов. В таком случае, когда вы нажимаете кнопку «Submit» (Отправить), HTTP-запрос POST отправляется на сервер.
HTTP и RESTful веб-сервисы
HTTP обеспечивает базовый уровень для создания веб-сервисов. Поэтому важно понимать HTTP. Вот несколько ключевых абстракций.
Ресурс
Ресурс — это ключевая абстракция, на которой концентрируется протокол HTTP. Ресурс — это все, что вы хотите показать внешнему миру через ваше приложение. Например, если мы пишем приложение для управления задачами, экземпляры ресурсов будут следующие:
URI ресурса
Когда вы разрабатываете RESTful сервисы, вы должны сосредоточить свое внимание на ресурсах приложения. Способ, которым мы идентифицируем ресурс для предоставления, состоит в том, чтобы назначить ему URI — универсальный идентификатор ресурса. Например:
REST и Ресурсы
Важно отметить, что с REST вам нужно думать о приложении с точки зрения ресурсов:
Определите, какие ресурсы вы хотите открыть для внешнего мира
Используйте глаголы, уже определенные протоколом HTTP, для выполнения операций с этими ресурсами.
Вот как обычно реализуется служба REST:
Компоненты HTTP
HTTP определяет следующую структуру запроса:
Методы HTTP-запроса
Метод, используемый в HTTP-запросе, указывает, какое действие вы хотите выполнить с этим запросом. Важные примеры:
Код статуса ответа HTTP
Код состояния всегда присутствует в ответе HTTP. Типичные примеры:
Резюме
В статье приведен на верхнем уровне обзор архитектурного стиля REST. Подчеркивается тот факт, что HTTP является основным строительным блоком REST сервисов. HTTP — это протокол, который используется для определения структуры запросов и ответов браузера. Мы видели, что HTTP имеет дело главным образом с ресурсами, доступными на веб-серверах. Ресурсы идентифицируются с помощью URI, а операции над этими ресурсами выполняются с использованием глаголов, определенных протоколом HTTP.
Наконец, мы рассмотрели, как службы REST наилучшим образом используют функции, предлагаемые HTTP, для предоставления ресурсов внешнему миру. REST не накладывает никаких ограничений на форматы представления ресурсов или на определение сервиса.
Полноценный REST API для перфекционистов за 5 минут
Врачи говорят, что это взаимосвязано, мол перфекционизм — это стремление к совершенству, а простота позволяет подобраться к этому мифическому совершенству. Чем проще решение, тем меньше ошибок можно допустить, вот я и подсел. Я не стал с ними спорить и вместо того, что бы искать виновников моей истории, решил с этим жить и постараться повысить качество этой самой жизни.
Мир вокруг не идеален, сложную вещь сделать простой – невероятно сложно, поэтому всё чрезмерно усложнено. Людям нравится чувствовать себя профессионалами, поэтому они оперируют сложными терминами, когда в этом нет необходимости, так они ощущают свою значимость и заполняют пустоту, которая образовалась из-за страха потерянного времени.
Почему я должен постоянно вспоминать о том, общался я с этим человеком или нет? Мы уже перешли на ты? Я пишу какое-то коммерческое предложение и мне надо «Выкнуть» или это ответ на рядовое письмо и достаточно простого «вы». Почему мне постоянно надо расстраиваться, когда мне с порога совершенно незнакомый человек начинает «тыкать», как будто я его друг с курилки?
Я принципиально всем «выкаю» с маленькой буквы и я убеждён, что это имеет свою цену. Наверняка есть люди, которым не нравится, что их назвали с маленькой буквы и они не ответят на моё письмо.
Так зачем эти сложности?
Когда я упомянул про повышение качества жизни, я имел ввиду некоторые правила, которые помогают быстро принимать решения и не жалеть об этом. Например, я не использую сложные вещи постоянно, не общаюсь с людьми, которым нравится всё усложнять. Если вещь, которой вы пользуетесь каждый день – сложна, то это значит только то, что ей не уделили достаточно внимания. Если человек вам не может объяснить даже трудную для понимания тему простым языком, значит, он сам не до конца понимает о чём говорит.
Есть ещё кое-что, что имеет не менее важное значение – время. Время – бесценный ресурс, а выражение «Время — деньги» из уст умных людей вызывает у меня улыбку с разочарованием (выглядишь как идиот и чувствуешь себя так же). Я до сих пор не знаю ни одного миллиардера, которому деньги помогли прожить дольше других людей.
Введение
Речь пойдёт об инструменте, который позволит вам построить полноценный и простой в использовании REST API за минимальное количество времени. Называется он – Python Eve.
К сожалению в Интернете очень много инструкций на эту тему, но все они вводят в заблуждение. Начинающие разработчики, начитавшись подобных статей, думают, что REST API это GET/POST/PUT/DELETE. Заказчики думают, что это дело пары часов. А когда они встречаются вместе, происходят магия в виде Express.js/Mongoose/Passport и ещё кучи хлама, который течёт и временами блокирует event-loop. Всё это запускается с помощью какого-нибудь supervisor, потому что иногда падает и надо как-то перезапускать.
И всё бы ничего, но вчера у меня состоялся разговор с хабра-пользователем, который предложил воспользоваться «Express.js, MongoDB, Mongoose, Passport, отладчиком WebStorm’a и головой на плечах«. Похожие разговоры случались часто, поэтому я решил написать эту статью и «отсылать» ссылкой на неё.
Полноценный REST API?
Речь не только о реализации архитектурного стиля REST API, но и о протоколе HTTP, о валидации и кэшировании, о HATEOAS (wikipedia), о котором, похоже, вообще предпочитают не вспоминать. Затем нам понадобится фильтрация результатов и сортировка, постраничная навигация и частичное обновление записей. Потом мы задумаемся о целостности данных и условных запросах. Наверняка нам понадобится аутентификация, возможно захотим отображать данные не только в JSON, но и в XML. Это ещё про версионность и вложенные записи я не упомянул. Затем, как это обычно бывает, какой-то #$%$%^ начнёт долбить в наш могучий API с тяжёлым запросом и нам понадобится ограничить частоту запросов.
Даже если представить, что разработкой такого API займётся невероятно крутой разработчик с 3-мя мониторами, отладчиком WebStorm’a и головой на плечах, он затратит на это не просто много времени, а очень много. Поддержка кодовой базы будет обходиться дорого, а внедрение новых функций будет долгим.
Но мы с вами простоту – любим, а время – уважаем. Так приступим же!
Установка
Это обычный python-пакет, поэтому устанавливается он стандартным способом:
Если вас интересуют альтернативные методы установки, можете заглянуть в официальную документацию.
Быстрый старт
Перед тем, как мы начнём «творить» магию, нам понадобится база данных MongoDB. Если у вас её нет, вы можете воспользоваться любым бесплатным сервисом, например MongoLab. Регистрация займёт не больше минуты. После регистрации создайте базу данных и пользователя для этой базы.
Теперь давайте напишем минимальную версию нашего REST API. Для начала создадим главный файл run.py со следующим содержимым:
Теперь нам надо создать файл настроек settings.py:
На мой взгляд здесь всё достаточно просто и вопросов возникнуть не должно. Если это не так – добро пожаловать в комментарии. Полный список параметров конфигурации вы можете глянуть в документации.
Всё готово, запускаем:
Прелюдия
Вы уже подумали, что сейчас мы начнём безбожно «curlить», но я вынужден вас разочаровать. Мы, со свойственным нам перфекционизмом, воспользуемся инструментом автора, который обладает чувством прекрасного:
Инструмент называется HTTPie и ставится в один клик одну команду:
Игрища и забавы
HTTPie вызывается командой «http«. Для того, что бы отправить GET запрос к нашему API, выполним:
Благодаря HATEOAS мы видим, что у нас есть 2 ресурса: users и groups. Заглянем внутрь:
Давайте создадим пользователя johndoe:
Первое, на что стоит обратить внимание, это на нашу команду:
HTTPie увидел, что мы отправляем параметр username и превратил его в JSON:
Затем отправил нашему API методом POST. Давайте обратим внимание на ошибки:
Мы видим сразу весь список ошибок валидации и это здорово. Исправим их и выполним запрос повторно:
Ну вот, мы только что успешно создали нового пользователя. Давайте проверим так ли это:
Нет никаких сомнений, что это так. Настало время попробовать перезаписать (обратите внимание, не отредактировать, а перезаписать) пользователя. Делается это с помощью метода PUT, который надо указать явно (если не указать, будет выполнен POST):
Упс, ошибка. Я выше упоминал о целостности данных. Дело в том, что может произойти ситуация, в которой кто-то уже изменил запись, которую хотите поменять вы. И когда вы её отредактируете, то будете думать, что изменили одну запись, но на самом деле уже совсем другую.
Для того, что бы мы в такой ситуации не оказались, используется идентификатор ETag. В двух словах — это уникальный идентификатор, который генерируется Eve при каждом изменении записи. Используя этот идентификатор мы можем сказать нашему API, что хотим изменить запись только определённой версии и если она с тех пор была отредактирована, то наши изменения выполнены не будут. Делается это с помощью условного запроса с HTTP заголовком «If-Match«:
Обратите внимание каким образом мы передали HTTP заголовок HTTPie. Это не единственное, для чего может использоваться идентификатор ETag и условные запросы. Я здесь не буду останавливаться на этом, но советую вам ознакомиться с этой темой, если вы этого ещё не сделали.
Настало время создать новую группу. Для начала попробуем создать группу с несуществующим пользователем:
И действительно, пользователя с таким _id не существует. Обратите внимание как мы передаём список пользователей:
Подробнее вы можете почитать в официальной документации к HTTPie, которая такая же качественная, как и сам инструмент.
На этот раз мы укажем правильный _id:
«Усё добра», как говорила моя прабабушка. Проверим:
Прабабушка оказалась бы права. На этом можно было бы закончить, но мы поступим иначе.
Получим группу с пользователями, которые в неё входят, в развёрнутом виде:
Попросим тоже самое в XML:
Попробуем изменить только имя нашего пользователя (без полной перезаписи). Для этого воспользуемся HTTP методом PATCH:
Что сказала бы бабуля?
«Усё добра». Мне так понравилось, что я бы создавал пользователей пачками:
Найдём пользователя «John Doe» по его имени:
Отсортируем пользователей по их логину в обратном порядке:
Ну и наконец-то удалим всех пользователей:
Я бы не стал включать данную возможность в production. Указывается это в параметре RESOURCE_METHODS (стоит убрать из списка DELETE):
Заключение
Мы рассмотрели не все возможности Eve, но даже c учётом этого – получили законченный вариант REST API.
В данной статье я хотел обратить внимание на то, что настоящий RESTful сервис это гораздо больше, чем пара модулей для Node.js и в большинстве случаев нет необходимости разрабатывать такие вещи с нуля, а тем более писать статьи о том, как это сделать за один час. Достаточно взглянуть на историю развития проекта, что бы лишний раз убедиться в том, что пары часов/дней/недель недостаточно даже для хорошей команды.
Nicola Iarocci, автор Eve, создал прекрасный инструмент для быстрого развёртывания REST API, уделил этому достаточно внимания и сохранил простоту использования.
В данной статье вы узнаете, как создать простой REST API в PHP.
1. Обзор проекта
1.1 Что такое REST API?
REST API позволяет вашему приложению взаимодействовать с одним или несколькими различными приложениями, используя концепции REST.
1.2 Зачем нужен REST API?
Во многих приложениях REST API необходим, потому что это самый легкий способ создания, чтения, обновления или удаления информации между различными приложениями через Интернет или протокол HTTP. Эта информация представляется пользователю в одно мгновение, особенно если вы используете JavaScript для отображения данных на веб-странице.
1.3 Где используется REST API?
REST API может использоваться любым приложением, которое может подключаться к Интернету. Если данные из приложения могут быть созданы, прочитаны, обновлены или удалены с помощью другого приложения, это обычно означает, что используется REST API.
2. Файловая структура
3. Настройка базы данных
3.1 Создание таблицы категорий
3.2 Дамп данных для таблицы категорий
3.3 Создание таблицы товаров
3.4 Дамп данных для таблицы товаров
3.5 Подключение к базе данных
Приведенный ниже код показывает учетные данные базы данных и метод для получения подключения к базе данных с помощью PDO.
Создайте папку api и откройте её. Создайте папку config и в ней создайте файл database.php со следующим кодом.
4. Получение товаров
4.1 Создание объекта Product
Код ниже содержит класс с именем Product и несколько свойств. Также показан метод конструктора, который принимает соединение с базой данных.
4.2 Создание файла для чтения товаров
Код ниже содержит заголовки о том, кто может читать этот файл и какой тип содержимого он будет возвращать.
4.3 Подключение к базе данных и таблице товаров
Замените комментарий // подключение к базе данных будет здесь в файле read.php следующим кодом.
4.4 Чтение товаров из базы данных
Замените комментарий // чтение товаров будет здесь в файле read.php следующим кодом.
4.5 Создание метода read()
4.6 Уведомление пользователя о том, что товары не найдены
Замените комментарий // ‘товары не найдены’ будет здесь в файле read.php следующим кодом.
5. Создание товаров
5.1 Создание файла create.php
Откройте папку product и создайте в ней файл create.php со следующим содержимым.
5.2 Создание метода create()
6. Получение одного товара
6.1 Создание файла read_one.php
6.2 Создание метода readOne()
7. Обновление товара
7.1 Создание файла update.php
7.2 Создание метода update()
8. Удаление товара
8.1 Создание файла delete.php
Откройте папку product и создайте файл delete.php со следующим содержимым.
8.2 Создание метода delete()
9. Поиск товаров
9.1 Создание файла search.php
В папке product создайте файл search.php со следующим кодом.
9.2 Создание метода search()
10. Пагинация товаров
10.1 Создание файла read_paging.php
В папке product создайте файл read_paging.php со следующим кодом.
10.2 Создание файла core.php
Этот файл содержит нашу базовую конфигурацию, такую как базовый URL и переменные пагинации.
Откройте папку config и создайте в ней файл core.php со следующим содержимым.
10.3 Создание метода readPaging()
10.4 Создание метода count()
Так же в классе Product (файл product.php) добавьте метод count() для создания массива пагинации.
10.5 Получение массива пагинации
11. Получение категорий
11.1 Создание объекта Category
Откройте папку objects и создайте новый файл category.php со следующим кодом.
11.2 Создание файла read.php
Создайте новую папку category в корне, и в ней файл read.php со следующим кодом.
11.3 Создание метода read()
Если вам понравилась данная статья, рекомендую к прочтению создание регистрации и авторизации в php с использованием JWT.
Надеюсь, вам понравилась данная информация. Если вам интересна тема web-разработки, то можете следить за выходом новых статей в Telegram.
Принципы построения REST JSON API
Эта памятка писалась для внутренних нужд (открыть глаза менее опытным в вебе коллегам). Но, т.к. я насмотрелся велосипедов от довольно уважаемых, казалось бы, контор, — выкладываю на хабр. Мне кажется, многим будет полезно.
Зачем
Надеюсь, читающий уже понимает, зачем ему вообще нужен именно REST api, а не какой-нибудь монстр типа SOAP. Вопрос в том, зачем соблюдать какие-то стандарты и практики, если браузеры вроде бы позволяют делать что хочешь.
Структура запросов и ответов
Любой http-запрос начинается со строки
где 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, могут вызвать непредсказуемые плохо отлавливаемые баги.
В запросах обязательно указывается заголовок
Вызовы к API отличаются от прочих вызовов (например, обычной загрузки html страницы по данному URI) именно по наличию application/json в Accept.
В ответах 2хх с непустым телом обязательно наличие заголовка ответа
При наличии тела запроса также обязателен заголовок запроса
либо, при загрузке файлов,
и далее, в первой части
после чего для каждого файла
Если вы используете защиту от CSRF (а лучше бы вам её использовать), то удобнее передавать CSRF-токен в отдельном заголовке (типа X-CSRF-Token) для всех запросов, а не внедрять вручную в каждый запрос. Хранить CSRF токен в куках плохо по той причине, что куки можно украсть, в чём собственно и состоит суть CSRF атаки.
Структура URI
Нагородить можно всякое, но лучшая практика — чтобы все URI имели вид
ну, или если у вас api лежит в какой-то папке,
Для разбора части URI до знака вопроса можно использовать регулярку
Ведущий слэш обязателен, т.к. неизвестно, с какого URL будет осуществлён запрос.
Методы HTTP
GET /:entity/:id — getById
В случае успеха сервер возвращает 200 OK с полями объекта в формате JSON в теле ответа (без дополнительного оборачивания в какой-либо объект)
В случае, если объект с такими id не существует, сервер возвращает 404 Not Found
В ответе обязательно должны быть заголовки, касающиеся политики кэширования, т.к. браузеры активно кешируют GET и HEAD запросы. При остутствии какой-либо политики управления кэшем должно быть:
GET /:entity[?param1=. ¶m2=. ] — списочный get
Простой случай: в случае успеха сервер возвращает 200 OK с массивом объектов в формате JSON в теле ответа (т.е. ответ начинается с [ и заканчивается ] ).
Если массив получился пустой, всё равно вовзращается 200 OK с пустым масивом [] в теле ответа.
Более сложный вариант: возвращается объект, в одном из полей которого — искомый массив. В остальных полях — данные о пагинации, фильтры, счётчики и пр. Только держите это консистентным по всем api.
HEAD /:entity[/:id] — запрос заголовков
Полный аналог GET с таким же URI, но не возвращает тело ответа, а только HTTP-заголовки.
Реализация поддержки HEAD запросов веб-сервером обязательна.
Активно используется браузерами в качестве автоматических pre-flight запросов перед выполнением потенциально опасных, по их мнению, операций. Например, браузер Chrome активно кидается head-запросами для получения политик CORS при кросс-доменных операциях (виджеты и пр). При этом ошибка обработки такого head-запроса приведёт к тому, что основной запрос вообще не будет выполнен браузером.
Может использоваться для проверки существования объекта без его передачи (например, для больших объектов типа мультимедиа-файлов).
POST /:entity — создаёт новый объект типа :entity
В теле запроса должны быть перечислены поля объекта в формате JSON без дополнительного заворачивания, т.е.
В случае успеха сервер должен возвращать 201 Created с пустым телом, но с дополнительным заголовком
указывающим на месторасположение созданного объекта.
Возвращать тело ответа чаще всего не требуется, так как у клиента есть все необходимые данные, а 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 с дополнительным заголовком
Некешиуремый необязательный метод.
Обработка ошибок
Возвращаемые ошибки передаются с сервера на клиент как ответы со статусами 4хх (ошибка клиента) или 5хх (ошибка сервера). При этом описание ошибки, если оно есть, приводится в теле ответа в формате text/plain (без всякого JSON). Соответственно, передаётся заголовок ответа
Использовать 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, либо что версии протокола клиента и сервера не совпадают.
419 Authentication Timeout
Отправляется, если клиенту нужно пройти повторную авторизацию (например, протухли куки или CSRF токены). При этом на клиенте могут быть несохранённые данные, которые будут потеряны, если просто выкинуть клиента на страницу авторизации.
422 Unprocessable Entity
Запрос корректно разобран, но содержание запроса не прошло серверную валидацию.
Например, в теле запроса были указаны неизвестные серверу поля, или не были указаны обязательные, или с содержимым полей что-то не так.
Обычно это означает ошибку в введённых пользователем данных, но может также быть вызвано ошибкой на клиенте или несовпадением версий.
500 Internal Server Error
Возвращается, если на сервере вылетело необработанное исключение или произошла другая необработанная ошибка времени исполнения.
Всё, что может сделать клиент в этом случае — это уведомить пользователя и сделать console.error(err) для более продвинутых товарищей (админов, разработчиков и тестировщиков).
501 Not Implemented
Возвращается, если текущий метод неприменим (не реализован) к объекту запроса.
Ну вот, в общем-то, и всё. Спасибо за внимание!