API REST 404: неверный URI или недостающий ресурс? - PullRequest
178 голосов
/ 29 марта 2012

Я строю REST API, но столкнулся с проблемой.

Кажется, что принятая практика при разработке REST API заключается в том, что если запрошенный ресурс не существует, возвращается 404.

Однако, для меня это добавляет ненужную двусмысленность. HTTP 404 более традиционно ассоциируется с неверным URI. Таким образом, в действительности мы говорим "Либо вы попали в нужное место, но эта конкретная запись не существует, или нет такого места в Интернете! ... "

Рассмотрим следующий URI:

http://mywebsite/api/user/13

Если я получу 404 обратно, это потому, что Пользователь 13 не существует? Или это потому, что мой URL должен был:

http://mywebsite/restapi/user/13

В прошлом я только что возвращал NULL-результат с кодом ответа HTTP 200 OK, если запись не существует. Это просто и, на мой взгляд, очень чисто, даже если это не обязательно принятая практика. Но есть ли лучший способ сделать это?

Ответы [ 9 ]

80 голосов
/ 29 марта 2012

404 - это просто код ответа HTTP. Кроме того, вы можете предоставить телу ответа и / или другим заголовкам более значимое сообщение об ошибке, которое увидят разработчики.

46 голосов
/ 30 марта 2012

Используйте 404, если ресурс не существует.Не возвращайте 200 с пустым телом.

Это похоже на undefined против пустой строки (например, "") в программировании.Хотя они очень похожи, есть определенная разница.

404 означает, что в этом URI ничего не существует (как неопределенная переменная в программировании).Возвращение 200 с пустым телом означает, что что-то там существует и что-то сейчас просто пустое (как пустая строка в программировании).

404 не означает, что это был «плохой URI»».Существуют специальные коды HTTP, предназначенные для ошибок URI (например, 414 Request-URI Too Long).

17 голосов
/ 30 марта 2012

Как и в большинстве вещей, «это зависит». Но для меня ваша практика неплоха и не противоречит спецификации HTTP per se . Тем не менее, давайте проясним некоторые вещи.

Во-первых, URI должны быть непрозрачными. Даже если они непрозрачны для людей, они непрозрачны для машин. Другими словами, разница между http://mywebsite/api/user/13, http://mywebsite/restapi/user/13 совпадает с разницей между http://mywebsite/api/user/13 и http://mywebsite/api/user/14, т. Е. Не совпадает не тот же период. Таким образом, 404 будет полностью подходящим для http://mywebsite/api/user/14 (если такого пользователя нет), но не обязательно единственным подходящим ответом.

Вы также можете вернуть пустой ответ 200 или более явно ответ 204 (без содержимого). Это передало бы что-то еще клиенту. Это означало бы, что ресурс, обозначенный http://mywebsite/api/user/14, не имеет содержимого или по сути ничего. Это означает, что является таким ресурсом. Однако это не обязательно означает, что вы утверждаете, что в хранилище данных сохранен какой-то пользователь с идентификатором 14. Это ваше личное дело, а не то, что клиент делает запрос. Так что, если есть смысл моделировать свои ресурсы таким образом, продолжайте.

Существуют некоторые последствия для безопасности при предоставлении вашим клиентам информации, которые помогут им угадать законные URI. Возврат 200 промахов вместо 404 может дать клиенту подсказку, что хотя бы часть http://mywebsite/api/user верна. Вредоносный клиент может просто пытаться использовать разные целые числа. Но для меня злонамеренный клиент мог бы угадать часть http://mywebsite/api/user в любом случае. Лучшим средством было бы использование UUID. то есть http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 лучше, чем http://mywebsite/api/user/14. Делая это, вы можете использовать свою технику возврата 200-х, не отдавая много.

10 голосов
/ 07 августа 2012

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 и указав канонический URL в заголовке Location ответа. Это говорит клиенту, что для будущих запросов он должен использовать канонический URL.

8 голосов
/ 26 ноября 2014

Так что, по сути, звучит так, что ответ может зависеть от того, как сформирован запрос.

Если запрошенный ресурс формирует часть URI согласно запросу к http://mywebsite/restapi/user/13, а пользователь 13 не существует, то 404, вероятно, является подходящим и интуитивно понятным, потому что URI представляет несуществующего пользователя /объект / документ / и т.д..То же самое можно сказать о более безопасном методе, использующем GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 и приведенный выше аргумент api / restapi.

Однако, если запрошенный идентификатор ресурса был включен в заголовок запроса [включите ваш собственный пример], илидействительно, в URI в качестве параметра, например, http://mywebsite/restapi/user/?UID=13, тогда URI будет по-прежнему корректным (поскольку концепция ПОЛЬЗОВАТЕЛЯ действительно выходит при http://mywebsite/restapi/user/);и, следовательно, можно ожидать, что ответом будет 200 (с соответствующим подробным сообщением), потому что конкретного пользователя, известного как 13, не существует, а URI существует.Таким образом, мы говорим, что URI хорош, но запрос данных не имеет содержимого.

Лично 200 все еще не чувствует себя хорошо (хотя я ранее утверждал, что это так).Код ответа 200 (без подробного ответа) может привести к тому, что проблема не будет исследована, например, при отправке неверного идентификатора.

Лучше было бы отправить ответ 204 - No Content.Это соответствует описанию w3c * Сервер выполнил запрос, но ему не нужно возвращать тело сущности, и он может захотеть вернуть обновленную метаинформацию. * 1 На мой взгляд, путаница вызвана записью в Википедии, в которой указано 204 Нет содержимого - сервер успешно обработал запрос, но не возвращает никакого содержимого. Обычно используются в качестве ответа на успешный запрос удаления . Последнее предложение весьма спорно.Рассмотрите ситуацию без этого предложения, и решение легко - просто отправьте 204, если объект не существует.Есть даже аргумент для возврата 204 вместо 404, запрос был обработан, а контент не был возвращен!Однако имейте в виду, что 204-е не допускают содержание в теле ответа

Источники

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

7 голосов
/ 03 ноября 2012

Эта старая, но отличная статья ... http://www.infoq.com/articles/webber-rest-workflow говорит об этом ...

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

6 голосов
/ 20 ноября 2017

Это очень старый пост, но я столкнулся с подобной проблемой, и я хотел бы поделиться своим опытом с вами, ребята.

Я строю микросервисную архитектуру с остальными API. У меня есть некоторые остальные сервисы GET, они собирают данные из серверной системы на основе параметров запроса.

Я следовал за остальными документами разработки API и отправлял обратно HTTP 404 с совершенным сообщением об ошибке JSON клиенту, когда не было данных, соответствующих условиям запроса (например, была выбрана нулевая запись).

Когда не было данных для отправки клиенту, я подготовил идеальное сообщение JSON с внутренним кодом ошибки и т. Д., Чтобы проинформировать клиента о причине «Не найдено», и оно было отправлено обратно клиенту по HTTP 404. Это отлично работает.

Позже я создал клиентский класс rest API, который является простым помощником для сокрытия кода, связанного с HTTP-связью, и я использовал его постоянно, когда вызывал свои API отдыха из своего кода.

НО мне нужно было написать непонятный дополнительный код только потому, что HTTP 404 имел две разные функции:

  • реальный HTTP 404, когда оставшийся API недоступен в данном URL-адресе, он генерируется сервером приложений или веб-сервером, на котором запускается приложение оставшегося API
  • клиент возвращает HTTP 404 также, когда в базе данных нет данных, основанных на условии, где запрос.

Важное замечание: Мой обработчик ошибок API rest перехватывает все исключения, появляющиеся в фоновой службе, что означает, что в случае любой ошибки мой API rest всегда возвращает идеальное сообщение JSON с деталями сообщения.

Это первая версия моего вспомогательного метода клиента, который обрабатывает два разных ответа 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 может каким-то образом получать два вида HTTP 404, мне нужно проверить тело ответа в случае HTTP 404. Если я могу проанализировать тело ответа, то я уверен, что я получил ответ, в котором не было данных для отправки клиенту.

Если я не могу разобрать ответ, это означает, что я получил настоящий HTTP 404 с веб-сервера (а не из остального приложения API).

Это сбивает с толку, и клиентское приложение всегда должно делать дополнительный анализ, чтобы проверить истинную причину HTTP 404.

Честно говоря, мне не нравится это решение. Это сбивает с толку, необходимо постоянно добавлять дополнительный дерьмовый код клиентам.

Поэтому вместо использования HTTP 404 в этих двух разных сценариях я решил, что сделаю следующее:

  • Я больше не использую HTTP 404 в качестве HTTP-кода ответа в моем приложении отдыха.
  • Я собираюсь использовать HTTP 204 (без содержимого) вместо HTTP 404.

В этом случае код клиента может быть более элегантным:

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

Я думаю, что это решает эту проблему лучше.

Если у вас есть лучшее решение, поделитесь им с нами.

1 голос
/ 29 марта 2012

Унифицированный идентификатор ресурса - это уникальный указатель на ресурс. URI с плохой формой не указывает на ресурс, и поэтому выполнение GET для него не вернет ресурс. 404 означает, что сервер не нашел ничего, соответствующего Request-URI. Если вы указали неверный URI или неверный URI, это является вашей проблемой и причиной, по которой вы не попали на ресурс, будь то HTML-страница или IMG.

0 голосов
/ 04 октября 2018

Для этого сценария HTTP 404 является кодом ответа для ответа от API REST, например, 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...