REST API 404:不適切なURI、またはリソースが不足していますか?


219

REST APIを構築していますが、問題が発生しました。

REST APIの設計で受け入れられている慣行は、要求されたリソースが存在しない場合は404が返されることです。

しかし、私には、これは不必要なあいまいさを追加します。HTTP 404は、より伝統的に不良URIに関連付けられています。つまり、実際には「正しい場所に到達したが、その特定のレコードが存在しない、インターネット上にそのような場所がない!どちらがどこなのかわからない...」

次のURIを考えます。

http://mywebsite/api/user/13

404が返ってきたのは、ユーザー13が存在しないためですか?それとも私のURLがそうであったはずだからです:

http://mywebsite/restapi/user/13

以前はHTTP 200 OK、レコードが存在しない場合、レスポンスコードとともにNULL結果を返しました。それは単純であり、私の意見では、それが必ずしも受け入れられている実践でなくても、非常にクリーンです。しかし、これを行うより良い方法はありますか?



7
他の質問はURIクエリ文字列フォーマットに関連しているようです。404についての議論は私の質問に答えるのに十分ではありません。それは、404が実際に何を意味するのかを判断するためのより適切なまたは有用な方法があるかどうかです。投稿する前に確認しました。
ブライアンレイシー

ブラウザのコンソールがエラー404で構成されているのは正常ですか?正しいURIで通常の操作を行うが、リソースが見つからない場合。
Vasiliy Mazhekin 14

3
404は無効なURIを示すのではなく、リソースが見つかりませんを示します。ユーザー '13'がないか、リソース/ mywebsite / apiがない可能性があります。
ChrisV、2015年

回答:


101

404は単なるHTTP応答コードです。その上で、開発者に表示されるより意味のあるエラーメッセージを含むレスポンスボディやその他のヘッダーを提供できます。


7
意味あり。しかし、最初に404を返すことと、null応答で200を返すことのどちらから実際に利益が得られるのか疑問に思います。
ブライアンレイシー

15
nullを含む200を返す場合、HTTP仕様に違反しています。これを行うことはできますが、APIはRESTの「Uniformed Interface」制約に準拠しません。
2012年

5
...そしてレスポンスボディには有効なURIへのハイパーリンクを含める必要があります。ルートURIと、おそらくブックマークが1つまたは2つない場合、クライアントは常にサーバーから提供されたリンクをたどる必要あります。そうすれば、彼らがシステムの外で働くことに決めた正確な方法について、詳細なセマンティクスを発明する必要はありません。
fumanchu 2012年

@DarrylHebbesどういう意味ですか、Chromeデベロッパーコンソールの[ネットワーク]タブでリクエストを実行し、レスポンスの完全な内容を確認できます。
jaapz 14

空の結果を返すクエリに対してエラー(404など)を返すのは非常に悪いことです。データベースがそれを行うことはなく、通常、空の結果を予期しない状況としてユーザーに報告したくありません。例:次の30分の予定が設定されているかどうかを確認するためにAPIをクエリします。ない場合は、404を返さないでください。200OKと空の配列を返してください。
esmiralha

59

404リソースが存在しない場合に使用します。200空の体で戻らないでください。

これは、プログラミングにおけるundefinedvs空の文字列(例:)に似てい""ます。非常に似ていますが、明らかに違いがあります。

404そのURIには何も存在しないことを意味します(プログラミングにおける未定義の変数のように)。200空の本文で戻るということは、そこに何かが存在し、現在何かがちょうど空であることを意味します(プログラミングにおける空の文字列のように)。

404それが「悪いURI」だったという意味ではありません。URIエラー(など414 Request-URI Too Long)を対象とした特別なHTTPコードがあります。


1
ねえ、私はあなたが何かに夢中かもしれないと思います。「41」の1つを返すほうが理にかなっているのではないでしょうか。リクエストされたリソースに問題がある場合のエラー?たとえば、410 Gone「要求されたリソースはサーバーで使用できなくなり、転送先アドレスが不明です」を意味します。-(w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11を参照)
ブライアンレイシー

実際、上記で参照したリソースは、「サーバーが状態が永続的であるかどうかを判断できない、または判断する機能がない場合は、代わりにステータスコード404(見つかりません)を使用する必要があります」とも述べています。そのため、おそらくそれは、必ずしも最良の選択肢のいずれか...ではありません
ブライアン・レイシー

2
41xエラーのどれもがあなたのユースケースに適しているとは思いません。未定義の文字列と空の文字列を比較するのが好きです。これは、私の回答でのポイントのより簡潔なバージョンです。違いはありますが、空の文字列がエラーであることを意味するものではありません。類推を拡張すると、次のString getName()ような実装を持つメソッドを作成できますreturn this.name == null ? "" : this.name。クライアントがそれを知ってthis.nameいることを望まない限り、それは間違いではありませんnull
jhericks

1
404は依然として最も適切なオプションです。エラーを受信するための具体的な詳細をユーザー/クライアントに通知するために、その404応答の本文を使用できます(推奨されます)。参照:stackoverflow.com/a/9999335/105484。あなたのケースでは、その本文を使用して、URIを/ restapi / user / USER_IDのように再フォーマットすることをユーザーに提案することができます
nategood

わかりました、ここでいくつかの良い点を見ることができますが、4XXはエラーコードですが、それはエラー状況ですか?クライアントはどのようにして404の発生を防ぐことができますか?
Krzysztof Kubicki

20

ほとんどの場合と同様に、「状況によって異なります」。しかし、私にとって、あなたの実践は悪くはなく、HTTP仕様自体に反することはありません。ただし、いくつかのことを明らかにしましょう。

まず、URIは不透明でなければなりません。彼らは人々に不透明でないとしても、彼らは機械には不透明です。換言すれば、差はhttp://mywebsite/api/user/13http://mywebsite/restapi/user/13差と同じであるhttp://mywebsite/api/user/13http://mywebsite/api/user/14同じ、すなわちないが同時期ではありません。したがって、404はhttp://mywebsite/api/user/14(そのようなユーザーがいない場合)完全に適切ですが、必ずしも唯一の適切な応答ではありません。

空の200応答、またはより明示的には204(コンテンツなし)応答を返すこともできます。これはクライアントに何か他のものを伝えます。によって識別されたリソースにhttp://mywebsite/api/user/14コンテンツがないか、本質的に何もないことを意味します。それはそこにいることを意味しているようなリソース。ただし、ID 14のデータストアに永続化されているユーザーがいると主張しているとは限りません。これは、クライアントがリクエストを行うという懸念ではなく、個人的な懸念です。したがって、リソースをそのようにモデル化することが理にかなっている場合は、先に進んでください。

クライアントが正当なURIを推測しやすくする情報をクライアントに提供することには、いくつかのセキュリティ上の影響があります。404の代わりにミスで200を返すことは、クライアントに少なくともhttp://mywebsite/api/user一部が正しいという手掛かりを与えるかもしれません。悪意のあるクライアントは、さまざまな整数を試し続けることができます。しかし、私にとって、悪意のあるクライアントはhttp://mywebsite/api/userとにかくその一部を推測することができます。より良い解決策は、UUIDを使用することです。つまりhttp://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66、より良いですhttp://mywebsite/api/user/14。そうすることで、200を返すというテクニックを使用して、多くのことを与えることはできません。


1
これは私が選択し、404の代わりに204を使用したソリューションです。私にとって、204は「コードは見つかりましたが、結果が見つかりませんでした」、404は「実行するコードが見つからなかった」という意味です。間違った道?」
マットサンダース

11

技術的に404 Not Foundは、現在URIがリソースにマップされていないことを意味します。あなたの例でhttp://mywebsite/api/user/13は、404を返すリクエストを解釈して、このURLがリソースにマップされたことないことを意味します。クライアントにとって、それは会話の終わりであるべきです。

あいまいさの懸念に対処するために、他の応答コードを提供してAPIを拡張できます。たとえば、クライアントがGETリクエストをurlに発行できるようにしたい場合、http://mywebsite/api/user/13クライアントが正規のurlを使用する必要があることを伝えたいとしますhttp://mywebsite/restapi/user/13。その場合は、301 Moved Permanentlyを返し、応答のLocationヘッダーに正規URLを指定して、永続的なリダイレクトを発行することを検討してください。これは、将来のリクエストでは正規URLを使用する必要があることをクライアントに伝えます。


11

つまり、本質的には、要求がどのように形成されるかに応じて答えが決まる可能性があるようです。

要求されたリソースが要求に従ってURIの一部を形成しhttp://mywebsite/restapi/user/13、ユーザー13が存在しない場合、URIは存在しないユーザー/エンティティ/ドキュメントなどを表すため、おそらく404が適切で直感的です。同じことは、http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66上記のGUID とapi / restapi引数を使用したより安全な手法にも当てはまります。

ただし、リクエストされたリソースIDがリクエストヘッダーに含まれている場合(独自の例を含む)、またはURIがパラメーターとして含まれている場合、たとえばhttp://mywebsite/restapi/user/?UID=13、URIは依然として正しいはずです(USERの概念はで終了するためhttp://mywebsite/restapi/user/)。したがって、13と呼ばれる特定のユーザーは存在しないがURIは存在するため、応答は200(適切な詳細メッセージを含む)であると合理的に期待できます。このように、URIは適切であると言いますが、データのリクエストにはコンテンツがありません。

個人的には、200はまだ正しくありません(以前はそうだと私は主張しましたが)。200応答コード(詳細応答なし)は、たとえば、誤ったIDが送信されたときに調査されない問題を引き起こす可能性があります。

より良いアプローチは、204 - No Content応答を送信することです。これは、サーバが要求を満たしていますが、エンティティボディを返す必要はありません。また、更新されたメタ情報を返すようにしたいかもしれません* W3Cの記述に準拠しています。* 1混乱、私の意見を述べるWikipediaのエントリによって引き起こされるで204ありません内容を-サーバーはリクエストを正常に処理しましたが、コンテンツを返していません。通常、成功した削除要求への応答として使用されます最後の文は非常に議論の余地があります。その文がない状況を考えてください。解決策は簡単です。エンティティが存在しない場合は、204を送信してください。404ではなく204を返す引数さえあり、リクエストは処理されており、コンテンツは返されていません!ただし、204は応答本文のコンテンツを許可しないことに注意してください。

出典

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html


9

それは非常に古い投稿ですが、私は同様の問題に直面しました。私の経験を皆さんと共有したいと思います。

REST APIを使用してマイクロサービスアーキテクチャを構築しています。残りのGETサービスがいくつかあります。これらのサービスは、リクエストパラメータに基づいてバックエンドシステムからデータを収集します。

残りのAPI設計ドキュメントに従い、クエリ条件に一致するデータがなかった(たとえば、ゼロのレコードが選択された)場合、完全なJSONエラーメッセージを含むHTTP 404をクライアントに送り返しました。

クライアントに送り返すデータがない場合、「Not Found」の理由をクライアントに通知するために、内部エラーコードなどを含む完全なJSONメッセージを準備し、HTTP 404でクライアントに送り返しました。正常に動作します。

後で、HTTP通信関連のコードを非表示にする簡単なヘルパーであるREST APIクライアントクラスを作成しました。コードからREST APIを呼び出すときは常にこのヘルパーを使用しました。

しかし、HTTP 404に2つの異なる機能があるため、混乱を招く追加コードを記述する必要がありました。

  • 残りのAPIが指定されたURLで使用できない場合の実際のHTTP404。これは、残りのAPIアプリケーションが実行されるアプリケーションサーバーまたはWebサーバーによってスローされます。
  • クエリのwhere条件に基づいてデータベースにデータがない場合も、クライアントはHTTP 404を返します。

重要:残りのAPIエラーハンドラーがすべての例外をキャッチし、バックエンドサービスに表示されます。つまり、エラーが発生した場合、残りのAPIは常にメッセージの詳細を含む完全なJSONメッセージを返します。

これは、2つの異なるHTTP 404応答を処理するクライアントヘルパーメソッドの最初のバージョンです。

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

しかし、私のJavaまたはJavaScriptクライアントは2種類のHTTP 404を何らかの方法で受信できるため、HTTP 404の場合は応答の本文を確認する必要があります。応答の本文を解析できれば、確実に応答が返されますクライアントに送り返すデータはありません。

応答を解析できない場合は、Webサーバーから(残りのAPIアプリケーションからではなく)実際のHTTP 404が返されたことを意味します。

これは非常にわかりにくく、クライアントアプリケーションは常にHTTP 404の実際の理由をチェックするために追加の解析を行う必要があります。

正直なところ、私はこのソリューションが好きではありません。混乱を招き、常にクライアントにでたらめなコードを追加する必要があります。

したがって、この2つの異なるシナリオでHTTP 404を使用する代わりに、以下を実行することにしました。

  • 残りのアプリケーションでHTTP 404を応答HTTPコードとして使用しなくなりました。
  • HTTP 404の代わりにHTTP 204(コンテンツなし)を使用します。

その場合、クライアントコードはよりエレガントになります。

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

これはその問題をよりよく処理すると思います。

より良い解決策があれば、私たちと共有してください。


Apache HttpClientメソッドは、204応答で例外をスローしません。クライアントがビジネスオブジェクト(モデル)に応答するだけの場合、nullが返されます。機能しますが、エンドユーザーが204の状況を検出するのは困難です。
chrisinmtown

それはすべて良いことですが、問題は、404のifステートメントをどれくらいの頻度で書くかです。そしてそれは何でしょうか?一方、エラーjsonを含む可能性のある404を定義するAPIを呼び出す場合は、この場合にのみ処理できます。
最大

zappee同意します。データを返すサービスがあります。しかし、データが壊れている状況があります。リクエストURLは正しい(404ではない)が、実際には論理エラー(500)ではないため、204が最も適しているようです。メッセージ本文の欠如については本当に迷惑です。間違いなく、私は理由をヘッダーに挿入することができました。
cs94njw


1

Uniform Resource Identifierは、リソースへの一意のポインタです。不適切な形式のURIはリソースを指さないため、GETを実行してもリソースは返されません。404は、サーバーがRequest-URIに一致するものを見つけられなかったことを意味します。間違ったURIまたは間違ったURIを入力した場合、それが問題であり、HTMLページでもIMGでもリソースにアクセスできなかった理由です。


0

このシナリオでは、HTTP 404はREST APIからの応答の応答コードです400、401、404、422などの処理できないエンティティ

例外処理を使用して、完全な例外メッセージを確認してください。

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

この例外ブロックは、REST APIによってスローされた適切なメッセージを提供します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.