1. 概要

このチュートリアルでは、SpringアプリケーションでのPOSTリクエストに対するHTTP応答コード415UnsupportedMediaTypeの原因と解決策を紹介します。

2. 裏話

私たちの古いビジネス顧客の1人が、彼らの製品用の新しいデスクトップアプリケーションを設計および開発するように私たちに依頼しました。 このアプリケーションの目的は、ユーザーを管理することです。 私たちはこれまでこの製品に取り組んだことがありません。

タイムラインが厳しいため、しばらく前に作成された既存のバックエンドAPIのセットを使用することにしました。 私たちの目の前の課題は、これらのAPIのドキュメントがあまり広範ではないことです。その結果、利用可能なAPIエンドポイントとそのメソッドについてのみ確信が持てます。 そのため、サービスには触れないことにしました。代わりに、このサービスからAPIを使用するアプリケーションの開発を開始します。

3. APIリクエスト

私たちのアプリケーションはAPIの消費を開始しました。 APIを実行して、すべてのユーザーを取得しましょう。

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

フラ! APIは、正常な応答で応答しました。 その後、個々のユーザーをリクエストしましょう。

curl -X GET https://baeldung.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://baeldung.service.com/user/

その結果、HTTPステータス415のエラー応答を受け取りました。

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

「なぜこのエラーが発生するのか」を理解する前に、「このエラーとは何ですか?」を調べる必要があります。

4. ステータスコード415:サポートされていないMediaType

仕様RFC7231タイトルHTTP/1.1セマンティクスおよびコンテンツセクション6.5.13によると:

415(サポートされていないメディアタイプ)ステータスコードは、ペイロードがターゲットリソースでこのメソッドでサポートされていない形式であるため、オリジンサーバーが要求の処理を拒否していることを示します。

仕様が示すように、選択したメディアタイプはAPIでサポートされていません。メディアタイプとしてJSONを選択した理由は、GETリクエストからの応答が原因でした。 応答データ形式はJSONでした。 したがって、POSTリクエストはJSONも受け入れると想定しました。 しかし、その仮定は間違っていることが判明しました。

APIでサポートされている形式を見つけるために、サーバー側のバックエンドコードを掘り下げることにしました。そして、API定義を見つけました。

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

これは、APIがXML形式のみをサポートすることを非常に明確に説明しています。 ここで疑問に思うかもしれません。Springのこの「consumes」要素の目的は何ですか?

Springフレームワークdocumentationによると、「used」要素の目的は次のとおりです。

マップされたハンドラーによって使用できるメディアタイプによってプライマリマッピングを絞り込みます。 1つ以上のメディアタイプで構成され、そのうちの1つはリクエストのContent-Typeヘッダーと一致する必要があります

5. 解像度

問題を解決するために私たちの前に2つのオプションがあります。 最初のオプションは、サーバーが期待するものに応じて要求ペイロード形式を変更することです。 2番目のオプションは、JSON形式のサポートを開始するようにAPIリクエストを更新することです。

5.1. リクエストのペイロードをXMLに変更します

最初のオプションは、JSONではなくXML形式でリクエストを送信することです。

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

残念ながら、上記のリクエストの結果として同じエラーが発生します。 覚えていると思いますが、質問に、APIの「consumes」要素の目的は何ですか。 これは、ヘッダーの1つ(「Content-Type」)が欠落しているという方向を示しています。 今回はヘッダーがない状態でリクエストを送信しましょう。

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

今回はお返事に成功しました。 ただし、クライアント側のアプリケーションがサポートされている形式でリクエストを送信できない場合があります。 この種のシナリオでは、物事を比較的柔軟にするために、サーバーに変更を加える必要があります。

5.2. サーバー上のAPIを更新します

お客様がバックエンドサービスの変更を許可することを決定したとします。 上記の2番目のオプションは、APIリクエストを更新してJSON形式の受け入れを開始することです。 APIリクエストを更新する方法にはさらに3つのオプションがあります。 それぞれを1つずつ見ていきましょう。

最初で最もアマチュアなオプションは、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://baeldung.service.com/user/'

応答は成功します。 ただし、XML形式でリクエストを送信している既存のすべてのクライアントで、415のサポートされていないメディアタイプエラーが発生し始めるという状況に直面します。

2番目のやや簡単なオプションは、リクエストペイロードですべてのフォーマットを許可することです

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

JSON形式でリクエストすると、応答は成功します。 ただし、ここでの問題は、柔軟性が高すぎるということです。 広範囲のフォーマットが許可されることを望んでいません。 これにより、コードベース全体(クライアント側とサーバー側の両方)で一貫性のない動作が発生します。

3番目の推奨オプションは、クライアント側アプリケーションが現在使用している形式を具体的に追加することです。 APIはすでにXML形式をサポートしているため、既存のクライアント側アプリケーションがあるため、そのままにしておきます。 XMLをAPIに送信します。 APIがアプリケーション形式もサポートするようにするために、簡単なAPIコードの変更を行います。

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

JSON形式でリクエストを送信すると、応答は成功します。 これは、この特定のシナリオで推奨される方法です。

6. 結論

この記事では、415 Unsupported Media Typeエラーを回避するために、「Content-Type」ヘッダーをクライアント側のアプリケーション要求から送信する必要があることを学びました。 また、RFCは、POST要求の送信中にこのエラーを回避するために、クライアント側アプリケーションとサーバー側アプリケーションの「Content-Type」ヘッダーが同期している必要があることを明確に説明しています。

この記事のすべてのコードは、GitHubから入手できます。