Czy poprawne jest zwracanie 404, gdy nie znaleziono zasobu REST?

Powiedzmy, że mam prosty zasób Jersey odpoczynku w następujący sposób:

@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();
        }
    }

}

Bazując na powyższym kodzie, czy poprawne jest zwracanie NOT_FOUND statusu (404), Czy powinienem zwracać 204, albo jakiś inny bardziej odpowiedni kod?

Wielkie dzięki z góry!

Author: Paul Samsotha, 2014-11-10

3 answers

Odpowiedź 404 w tym przypadku jest dość typowa i łatwa do wykorzystania przez użytkowników API.

Jeden problem polega na tym, że trudno jest Klientowi stwierdzić, czy otrzymał 404 z powodu nie odnalezienia konkretnej jednostki, lub z powodu problemu strukturalnego w URI. W twoim przykładzie /foos/5 może zwrócić 404, ponieważ foo z id = 5 nie istnieje. Jednak /food/1 zwróci 404, nawet jeśli foo z id=1 istnieje (ponieważ {[3] } jest błędnie napisane). Innymi słowy, 404 oznacza albo źle skonstruowany URI, albo odniesienie do nieistniejącego zasobu.

Inny problem pojawia się, gdy masz URI, który odwołuje się do wielu zasobów. Dzięki prostej odpowiedzi 404, klient nie ma pojęcia, które z odwołanych zasobów nie zostało znalezione.

Oba te problemy można częściowo złagodzić, zwracając dodatkowe informacje w ciele odpowiedzi, aby poinformować dzwoniącego dokładnie, co nie zostało znalezione.

 36
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-17 14:57:38

Tak, zwracanie 404 jest dość powszechne, ponieważ zasób nie został znaleziony. Tak jak strona internetowa, gdy jej nie znajdziesz, dostajesz 404. To nie tylko odpoczynek, ale standard HTTP.

Każdy zasób powinien mieć adres URL. Adresy URL nie muszą być statyczne, mogą być template . Możliwe jest więc, że żądany adres URL nie ma zasobu. Obowiązkiem serwera jest rozbicie adresu URL z szablonu w celu wyszukania zasobu. Jeśli zasobu nie ma, to jest "Nie Znaleziono"

Oto z HTTP 1.1 spec

404 Nie Znaleziono

Serwer nie znalazł niczego pasującego do Request-URI. Nic nie wskazuje na to, czy warunek jest tymczasowy lub stały. Kod stanu 410 (Gone) powinien być używany, jeśli serwer wie, poprzez jakiś wewnętrznie konfigurowalny mechanizm, że stary zasób jest trwale niedostępny i nie ma adresu przekierowania. Ten kod stanu jest powszechnie używany gdy serwer nie chce dokładnie ujawnić, dlaczego żądanie zostało odrzucone lub gdy nie ma innej odpowiedzi.


Oto 204

204 No Content

Serwer spełnił żądanie, ale nie musi zwracać podmiotu-ciała i może chcieć zwrócić zaktualizowane metainformacje. Odpowiedź może zawierać nowe lub zaktualizowane metainformacje w postaci entity-headerów, które jeśli są obecne, powinny być powiązane z żądany wariant.

Jeśli klient jest agentem użytkownika, nie powinien zmieniać widoku dokumentu z tego, który spowodował wysłanie żądania. Ta odpowiedź ma przede wszystkim na celu umożliwienie wprowadzania akcji bez powodowania zmiany w aktywnym widoku dokumentu agenta użytkownika, chociaż wszelkie nowe lub zaktualizowane metainformacje powinny być stosowane do dokumentu aktualnie w aktywnym widoku agenta użytkownika.

Odpowiedź 204 nie może zawierać treści wiadomości, a zatem jest zawsze kończy się pierwszą pustą linią po polach nagłówka.

Normalnie 204 będzie używany, gdy reprezentacja została zaktualizowana lub utworzona i nie ma potrzeby, aby wysłać ciało odpowiedzi z powrotem. W przypadku postu możesz odesłać tylko lokalizację nowo utworzonego zasobu. Coś jak

@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) odeśle odpowiedź z nowo utworzonym URI w nagłówku Location.


Dodanie do pierwszej części. Musisz tylko pamiętać każde żądanie klienta jest żądaniem dostępu do zasobu, czy to tylko po to, aby go uzyskać, czy zaktualizować za pomocą PUT. A zasobem może być wszystko na serwerze. Jeśli zasób nie istnieje, ogólną odpowiedzią byłoby poinformowanie klienta, że nie możemy znaleźć tego zasobu.

Aby rozwinąć na swoim przykładzie. Powiedzmy FooService accsses DB. Każdy wiersz w bazie danych może być uważany za zasób. Każdy z tych wierszy (zasobów) ma unikalny adres URL, np. foo/db/1 może zlokalizować wiersz z podstawowym klucz 1. Jeśli nie można znaleźć identyfikatora, to zasób to "nie znaleziono"

 17
Author: Paul Samsotha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-10 14:46:06

A 4XX Kod Błędu oznacza błąd po stronie klienta.
Gdy zażądasz statycznego zasobu jako obrazu lub strony html, zwracając 404 odpowiedź ma sens jako:

Kod odpowiedzi błędu klienta HTTP 404 Not Found wskazuje, że serwer nie może znaleźć żądanego zasobu. Linki prowadzące do 404 strony są często nazywane uszkodzonymi lub martwymi linkami i mogą podlegać linkowi zgnilizna.

Ponieważ dostarczasz klientom kilka metod odpoczynku, możesz polegaj na metodach HTTP, ale nie powinieneś uważać usług REST za proste zasoby.
W przypadku klientów odpowiedź na błąd w metodzie REST jest często obsługiwana w pobliżu błędów innych procesów.

Na przykład, aby wyłapać błędy podczas wywoływania REST lub gdzie indziej, klienci mogą użyć catchError() z RxJS.

Możemy napisać kod (w TypeScript/Angular 2 dla przykładowego kodu) w ten sposób, aby delegować przetwarzanie błędów do funkcji :

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

Problem polega na tym, że każdy błąd HTTP (5XX lub 4XXX) zakończy się w wywołaniu zwrotnym catchError().
Może to naprawdę sprawić, że odpowiedzi REST API wprowadzą klientów w błąd.

Jeśli wykonamy równolegle z językiem programowania, możemy rozważyć 5XX/4XX jako przepływ WYJĄTKÓW.
Ogólnie rzecz biorąc, nie rzucamy wyjątku tylko dlatego, że dane nie zostały znalezione, rzucamy go, ponieważ dane nie zostały znalezione i że dane te zostały znalezione .
Dla REST API, powinniśmy podążać za ta sama logika.

Jeśli byt może nie zostać znaleziony, zwrócenie OK w obu przypadkach jest w porządku:

@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();

}

Klient mógł obsłużyć wynik w zależności od tego, czy wynik jest obecny lub brakujący.
Nie sądzę, aby zwracanie 204 przyniosło jakąś użyteczną wartość.
dokumentacja HTTP 204 stwierdza, że:

Klient nie musi odchodzić od bieżącej strony.

Ale Prośba o odpoczynek i nie tylko szczególnie przez metodę GET nie oznacza to, że klient jest o zakończeniu przepływu pracy(to ma więcej sensu w metodzie POST/PUT).

Dokument dodaje również:

Powszechnym przypadkiem użycia jest zwrócenie 204 w wyniku żądania PUT, aktualizowanie zasobu bez zmiany bieżącej zawartości strony wyświetlane użytkownikowi.

Nie jesteśmy w tym przypadku.

Niektóre specyficzne kody HTTP do klasycznego przeglądania stron z zwraca kody REST API (201, 202, 401 itd...), ale nie zawsze tak jest. Więc w tych przypadkach, zamiast skręcania oryginalnych kodów, chciałbym zachować je proste, używając bardziej ogólnych kodów : 200, 400.

 2
Author: davidxxx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-18 18:42:39