web-dev-qa-db-ja.com

RESTリソースが見つからない場合に404​​を返すのは正しいですか?

次のような単純なJersey RESTリソースがあるとします。

@Path("/foos")
public class MyRestlet
        extends BaseRestlet
{

    @GET
    @Path("/{fooId}")
    @Produces(MediaType.APPLICATION_XML)
    public Response getFoo(@PathParam("fooId") final String fooId)
            throws IOException, ParseException
    {
        final Foo foo = fooService.getFoo(fooId);

        if (foo != null)
        {
            return Response.status(Response.Status.OK).entity(foo).build();
        }
        else
        {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

}

上記のコードに基づいて、NOT_FOUNDステータス(404)を返すのは正しいですか、それとも204またはその他の適切なコードを返す必要がありますか?

33
carlspring

この場合の404応答は非常に一般的であり、APIユーザーにとっては使いやすいものです。

1つの問題は、特定のエンティティが見つからないため、またはURIの構造上の問題のために、クライアントが404を受け取ったかどうかを判断するのが難しいことです。この例では、id = 5のfooが存在しないため、/foos/5は404を返す場合があります。ただし、/food/1のfooが存在する場合でも、id=1は404を返します(foosのスペルが間違っているため)。言い換えると、404は、不適切に構成されたURIまたは存在しないリソースへの参照のいずれかを意味します。

別の問題は、複数のリソースを参照するURIがある場合に発生します。単純な404応答では、クライアントは、参照されたリソースのどれが見つからなかったかを知りません。

これらの問題は両方とも、応答本文に追加情報を返すことで部分的に軽減でき、発信者に何が見つからなかったかを正確に知らせることができます。

52
Rob

はい、リソースが見つからない場合に404​​を返すことはよくあります。 Webページのように、見つからない場合は404を取得します。これはRESTだけでなく、HTTP標準です。

すべてのリソースにはURLの場所が必要です。 URLは静的である必要はなく、 templated にすることができます。そのため、実際にリクエストされたURLにリソースがない可能性があります。リソースを探すためにテンプレートからURLを分解することはサーバーの義務です。リソースが存在しない場合は、 "Not Found"

HTTP 1.1仕様

404見つかりません

サーバーは、Request-URIに一致するものを見つけられませんでした。状態が一時的なものか永続的なものかは示されていません。 410(Gone)ステータスコードは、サーバーが内部で構成可能なメカニズムを介して、古いリソースが永続的に使用不可であり、転送アドレスがないことを知っている場合に使用する必要があります。このステータスコードは、サーバーが要求が拒否された正確な理由を明らかにしたくない場合、または他の応答が適用できない場合に一般的に使用されます。


ここに204があります

204コンテンツなし

サーバーは要求を満たしましたが、エンティティ本体を返す必要はなく、更新されたメタ情報を返したい場合があります。応答には、エンティティヘッダーの形式で新規または更新されたメタ情報が含まれる場合があります。これは、存在する場合、要求されたバリアントに関連付けられる必要があります。

クライアントがユーザーエージェントである場合、リクエストの送信を引き起こしたドキュメントビューからドキュメントビューを変更すべきではありません。この応答は主に、ユーザーエージェントのアクティブなドキュメントビューを変更せずにアクションの入力を許可することを目的としていますが、新規または更新されたメタ情報は、ユーザーエージェントのアクティブビューに現在あるドキュメントに適用される必要があります。

204応答にはメッセージ本文を含めてはならない(MUST NOT)ため、ヘッダーフィールドの後の最初の空行で常に終了します。

通常、表現が更新または作成され、応答本文を送り返す必要がない場合、204が使用されます。 POSTの場合、新しく作成されたリソースの場所だけを送り返すことができます。何かのようなもの

_@POST
@Path("/something")
@Consumes(...)
public Response createBuzz(Domain domain, @Context UriInfo uriInfo) {
    int domainId = // create domain and get created id
    UriBuilder builder = uriInfo.getAbsolutePathBuilder();
    builder.path(Integer.toString(domainId));  // concatenate the id.
    return Response.created(builder.build()).build();
}
_

created(URI)は、Locationヘッダーに新しく作成されたURIを含む応答を返します。


最初の部分に追加します。クライアントからのすべてのリクエストは、リソースを取得するだけであるか、PUTで​​更新するかに関係なく、リソースにアクセスするためのリクエストであることに留意する必要があります。また、リソースはサーバー上の任意のものにすることができます。リソースが存在しない場合、一般的な応答は、そのリソースが見つからないことをクライアントに伝えることです。

あなたの例を拡張します。 FooServiceがDBにアクセスするとします。データベースの各行はリソースと見なすことができます。そして、それらの行(リソース)のそれぞれは、_foo/db/1_のような一意のURLを持ち、主キー1を持つ行を見つけます。IDが見つからない場合、そのresourceis "Not Found"

20
Paul Samsotha

_4XX_エラーコードは、クライアント側からのエラーを意味します。
画像またはhtmlページとして静的リソースをリクエストすると、 _404_ response が返されます:

HTTP 404 Not Foundクライアントエラー応答コードは、サーバーが要求されたリソースを見つけられないことを示します。 404ページにつながるリンクは、壊れたリンクまたはデッドリンクと呼ばれることが多く、リンクが腐敗する可能性があります。

クライアントにいくつかのRESTメソッドを提供する場合、HTTPメソッドに依存しますが、RESTサービスを単純​​なリソースと見なさないでください。
クライアントの場合、RESTメソッドのエラー応答は、他の処理のエラーの近くで処理されることがよくあります。

たとえば、REST呼び出し中または他の場所でエラーをキャッチするには、クライアントはcatchError() of RxJS を使用できます。

エラー処理を関数に委任するために、この方法でコードを(サンプルコードのTypeScript/Angular 2で)書くことができます。

_return this.http
  .get<Foo>("/api/foos")
  .pipe(
      catchError(this.handleError)
  )
  .map(foo => {...})
_

問題は、HTTPエラー(5XXまたは4XXX)がcatchError()コールバックで終了することです。
実際にREST API応答がクライアントにとって誤解を招く可能性があります。

プログラミング言語と並行して行う場合、5XX/4XXを例外フローと見なすことができます。
一般に、データが見つからないという理由だけで例外をスローするのではなく、データが見つからないため、そのデータが見つかったとしてスローします
REST API)の場合、同じロジックに従う必要があります。

エンティティが見つからない場合、2つのケースでOKを返すことはまったく問題ありません。

_@GET
@Path("/{fooId}")
@Produces(MediaType.APPLICATION_XML)
public Response getFoo(@PathParam("fooId") final String fooId)
        throws IOException, ParseException {
    final Foo foo = fooService.getFoo(fooId);

    if (foo != null){
        return Response.status(Response.Status.OK).entity(foo).build();
    }

    return Response.status(Response.Status.OK).build();

}
_

クライアントは、結果の有無に応じて結果を処理できます。
_204_を返すことは有用な価値をもたらすとは思わない。
HTTP _204_ documentation は次のように述べています:

クライアントは現在のページから離れる必要はありません。

ただし、RESTリソース、より具体的にはGETメソッドによる要求は、クライアントがワークフローを終了しようとしていることを意味しません(POST/PUTメソッドでより意味があります)。

文書には次も追加されます。

一般的な使用例は、ユーザーに表示されるページの現在のコンテンツを変更せずに、リソースを更新するPUT要求の結果として204を返すことです。

私たちは本当にこの場合ではありません。

古典的なブラウジング用の特定のHTTPコードは、REST API(201、202、401など...)の戻りコードと細かく一致しますが、常にそうとは限りません。 、元のコードをねじるのではなく、より一般的なコードを使用してシンプルにすることをお勧めします:_200_、_400_。

2
davidxxx