Подтвердить что ты не робот

Правильно ли возвращать 404, когда ресурс REST не найден?

Допустим, у меня есть простой (Джерси) ресурс 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, или какой-то другой более подходящий код?

4b9b3361

Ответ 1

Ответ 404 в этом случае довольно типичен и удобен для пользователей API.

Одна из проблем заключается в том, что клиенту трудно определить, получили ли они 404 из-за того, что конкретная сущность не найдена, или из-за структурной проблемы в URI. В вашем примере /foos/5 может возвращать 404, потому что foo с id = 5 не существует. Однако, /food/1 вернет 404, даже если foo с id=1 существует (потому что foos написано неправильно). Другими словами, 404 означает либо плохо сконструированный URI, либо ссылку на несуществующий ресурс.

Другая проблема возникает, когда у вас есть URI, который ссылается на несколько ресурсов. С простым ответом 404 клиент не знает, какой из ссылочных ресурсов не найден.

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

Ответ 2

Да, довольно часто возвращать 404 для ресурса, который не найден. Так же, как веб-страница, когда она не найдена, вы получаете 404. Это не просто REST, а HTTP-стандарт.

Каждый ресурс должен иметь URL-адрес. URL-адреса не обязательно должны быть статическими, они могут быть templated. Таким образом, у фактического запрашиваемого URL-адреса может отсутствовать ресурс. Обязанностью сервера является разбивка URL-адреса из шаблона для поиска ресурса. Если ресурс не существует, то он "не найден"

Здесь из HTTP 1.1 spec

404 Не найдено

Сервер не нашел ничего, что соответствовало бы Request-URI. Не указывается, является ли это условие временным или постоянным. Код состояния 410 (Gone) ДОЛЖЕН использоваться, если сервер через какой-то механизм, который может быть внутренне конфигурирован, знает, что старый ресурс постоянно недоступен и не имеет адреса пересылки. Этот код состояния обычно используется, когда сервер не хочет точно определить, почему запрос был отклонен, или когда другой ответ не применим.


Здесь для 204

204 Нет содержимого

Сервер выполнил запрос, но ему не нужно возвращать тело сущности и может захотеть вернуть обновленную метаинформацию. Ответ МОЖЕТ включать новую или обновленную метаинформацию в виде заголовков сущностей, которые, если они ДОЛЖНЫ быть связаны с запрошенным вариантом.

Если клиент является пользовательским агентом, он НЕ ДОЛЖЕН изменять вид своего документа из того, что вызвало отправку запроса. Этот ответ в первую очередь предназначен для того, чтобы разрешить вход для действий, не вызывая изменения в представлении активного документа пользователя, хотя любая новая или обновленная метаинформация ДОЛЖНА применяться к документу, находящемуся в настоящее время в активном представлении пользовательского агента.

Ответ 204 НЕ ДОЛЖЕН включать тело сообщения и, таким образом, всегда заканчивается первой пустой строкой после полей заголовка.

Обычно 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) отправит ответ с вновь созданным URI в заголовок Location.


Добавление к первой части. Вам просто нужно иметь в виду, что каждый запрос от клиента - это запрос на доступ к ресурсу, независимо от того, нужно ли его получить или обновить с помощью PUT. И ресурс может быть любым на сервере. Если ресурс не существует, тогда общий ответ будет заключаться в том, чтобы сообщить клиенту, что мы не можем найти этот ресурс.

Чтобы расширить свой пример. Скажем, FooService использует БД. Каждая строка в базе данных может считаться ресурсом. И каждая из этих строк (ресурсов) имеет уникальный URL-адрес, например foo/db/1 может найти строку с первичным ключом 1. Если идентификатор не найден, то этот ресурс "Not Found"

Ответ 3

A 4XX код ошибки означает ошибку со стороны клиента.
Когда вы запрашиваете статический ресурс как изображение или html-страницу, возврат 404 response имеет смысл как:

Код ответа на ошибку клиента HTTP 404 Not Found указывает, что сервер не может найти запрошенный ресурс. Ссылки, которые приводят к 404 страницы часто называются сломанными или мертвыми ссылками и могут быть связаны ссылками гниль.

Как вы предоставляете клиентам некоторые методы REST, вы полагаетесь на методы HTTP, но не должны рассматривать службы REST как простые ресурсы.
Для клиентов ответ об ошибке в методе REST часто обрабатывается рядом с ошибками других процессов.

Например, чтобы ловить ошибки во время вызовов REST или в другом месте, клиенты могли использовать catchError() 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 мы должны следовать той же логике.

Если объект не найден, возвращение 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 указано, что:

Клиент не должен уходить со своей текущей страницы.

Но запрос ресурса REST и, в частности, методом GET не означает, что клиент должен завершить рабочий процесс (что имеет большее значение с помощью методов POST/PUT).

В документе добавляется также:

Общим вариантом использования является возврат 204 в результате запроса PUT, обновление ресурса без изменения текущего содержимого страницы отображается пользователю.

Мы действительно не в этом случае.

Некоторые конкретные HTTP-коды для классического просмотра матче точно с кодами возврата REST API (201, 202, 401 и т.д. для...), но это не всегда так. Поэтому для этих случаев, а не для извращения исходных кодов, я бы предпочел сохранить их просто, используя более общие коды: 200, 400.