Перейти к основному содержимому

415 Неподдерживаемый MediaType в приложении Spring

· 5 мин. чтения

1. Обзор

В этом руководстве мы собираемся продемонстрировать причины и решения кода ответа HTTP 415 Unsupported MediaType для запросов POST в приложении Spring.

2. Предыстория

Один из наших старых бизнес-клиентов попросил нас спроектировать и разработать новое настольное приложение для его продукта. Целью этого приложения является управление пользователями. Мы никогда раньше не работали над этим продуктом.

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

3. API-запросы

Наше приложение начало использовать API. Давайте поэкспериментируем с API, чтобы получить всех пользователей:

curl -X GET https://foreach.service.com/user

Ура! API ответил успешным ответом. После этого давайте запросим отдельного пользователя:

curl -X GET https://foreach.service.com/user/{user-id}

И давайте проверим ответ:

{
"id": 1,
"name": "Jason",
"age": 23,
"address": "14th Street"
}

Кажется, это тоже работает. Поэтому все выглядит гладко. По ответу мы можем выяснить, что у пользователя есть следующие параметры: id , name , age и address .

Теперь попробуем добавить нового пользователя:

curl -X POST -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"}' https://foreach.service.com/user/

В результате мы получили ответ об ошибке с HTTP-статусом 415 :

{
"timestamp": "yyyy-MM-ddThh:mm:ss.SSS+00:00",
"status": 415,
"error": "Unsupported Media Type",
"path": "/user/"
}

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

4. Код состояния 415: неподдерживаемый тип носителя

Согласно спецификации RFC 7231, заголовок HTTP/1.1 Semantics and Content, раздел 6.5.13 :

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

Как следует из спецификации, выбранный нами тип мультимедиа не поддерживается API. Причиной выбора JSON в качестве типа мультимедиа был ответ на запросы GET. Формат данных ответа был в JSON. Следовательно, мы предполагали, что запрос POST также будет принимать JSON . Однако это предположение оказалось ошибочным.

Чтобы выяснить, какой формат поддерживается API, мы решили копаться в серверном бэкэнд-коде и находим определение API:

@PostMapping(value = "/", consumes = {"application/xml"})
void AddUser(@RequestBody User user)

Это довольно ясно объясняет, что API будет поддерживать только формат XML. Здесь может возникнуть вопрос: какова цель этого элемента « consumes » в Spring?

Согласно документации фреймворка Spring назначение элемента « consumes »:

  > Сужает первичное сопоставление по типам мультимедиа, которые могут использоваться сопоставленным обработчиком.  Состоит из одного или нескольких типов мультимедиа, один из которых должен соответствовать заголовку Content-Type запроса.

5. Разрешение

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

5.1. Изменить полезную нагрузку запроса на XML

Первый вариант — отправлять запросы в формате XML вместо JSON :

curl -X POST -d '<user><name>Abdullah</name><age>28</age><address>Apartment 2201</address></user>' https://foreach.service.com/user/

К сожалению, мы получаем ту же ошибку в результате вышеуказанного запроса. Если мы помним, мы задавали вопрос , какова цель этого элемента « consumes » в API. Это указывает нам на то, что один из наших заголовков (« Content-Type ») отсутствует . Отправим запрос, на этот раз с отсутствующим заголовком:

curl -X POST -H "Content-Type: application/xml" -d '<user><name>Abdullah</name><age>28</age><address>Apartment 2201</address></user>' https://foreach.service.com/user/

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

5.2. Обновите API на сервере

Предположим, что наш клиент решил разрешить нам изменять серверные службы. Второй вариант, как упоминалось выше, — обновить запрос API, чтобы начать принимать формат JSON. Есть еще три варианта того, как мы можем обновить запрос API. Давайте рассмотрим каждый, один за другим.

Первый и самый любительский вариант — заменить в API формат XML на JSON :

@PostMapping(value = "/", consumes = {"application/json"}) 
void AddUser(@RequestBody User user)

Снова отправим запрос из нашего клиентского приложения в формате JSON:

curl -X POST -H "Content-Type: application/json" -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"} https://foreach.service.com/user/'

Ответ будет успешным. Однако мы столкнемся с ситуацией, когда все наши существующие клиенты, отправляющие запросы в формате XML, теперь начнут получать ошибки 415 Unsupported Media Type.

Второй и несколько более простой вариант — разрешить все форматы в полезной нагрузке запроса :

@PostMapping(value = "/", consumes = {"*/*"}) 
void AddUser(@RequestBody User user

По запросу в формате JSON ответ будет успешным. Однако проблема здесь в том, что он слишком гибкий . Мы не хотим, чтобы был разрешен широкий спектр форматов. Это приведет к непоследовательному поведению всей кодовой базы (как на стороне клиента, так и на стороне сервера).

Третий и рекомендуемый вариант — добавить именно те форматы, которые в настоящее время используются клиентскими приложениями. Поскольку API уже поддерживает формат XML, мы сохраним его, так как существующие клиентские приложения отправляют XML в API. Чтобы API также поддерживал формат нашего приложения, мы внесем простое изменение в код API:

@PostMapping(value = "/", consumes = {"application/xml","application/json"}) 
void AddUser(@RequestBody User user

После отправки нашего запроса в формате JSON ответ будет успешным. Это рекомендуемый метод в данном конкретном случае.

6. Заключение

В этой статье мы узнали, что заголовок « Content-Type » должен быть отправлен из запроса приложения на стороне клиента, чтобы избежать ошибки 415 Unsupported Media Type. Кроме того, RFC четко объясняет, что заголовок « Content-Type » клиентского приложения и серверного приложения должен быть синхронизирован, чтобы избежать этой ошибки при отправке POST-запроса.

Весь код этой статьи доступен на GitHub .