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

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

Брайан Лейси
источник
Вероятно, дубликат. stackoverflow.com/questions/3821663/…
Спенсер Кормос
7
Другой вопрос, похоже, связан с форматами строк URI Query. Обсуждение 404 недостаточно для ответа на мой вопрос: есть ли более подходящий или полезный способ определить, что на самом деле означает 404? Я рассмотрел это перед публикацией.
Брайан Лейси
Это нормально, когда браузерная консоль содержит ошибки 404? Когда я делаю регулярные операции с правильным URI, но ресурс не найден.
Василий Мажекин
3
404 не указывает на неверный URI, он указывает на ресурс Not Found. Это может быть связано с отсутствием пользователя '13' или с отсутствием ресурса / mywebsite / api.
ChrisV

Ответы:

101

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

Роберт Леви
источник
7
Это имеет смысл. Я должен задаться вопросом, однако, действительно ли какая-то выгода от возвращения 404 в первую очередь от возврата 200 с нулевым ответом?
Брайан Лейси
15
Если вы возвращаете 200 с нулем, вы идете против спецификации HTTP. Вы можете сделать это, но тогда ваш API не будет придерживаться ограничения "Uniformed Interface" REST.
судится с
5
... и ваше тело ответа должно содержать гиперссылки на действительные URI. За исключением корневого URI и, возможно, одной или двух закладок, ваши клиенты всегда должны переходить по ссылкам, предоставленным им сервером. Тогда нет необходимости придумывать детальную семантику относительно того, как именно они решили работать вне системы.
fumanchu
@DarrylHebbes, что ты имеешь в виду? Я могу сделать запрос и просмотреть полное содержание ответа на вкладке Сеть консоли разработчика Chrome.
Яапз
Невероятно плохо возвращать ошибку (404 или что-то еще) для любого запроса, который возвращает пустые результаты. Никакая база данных не сделает этого, и обычно вы не хотите сообщать о пустом результате как о непредвиденном состоянии для пользователя. Пример: вы запрашиваете свой API, чтобы проверить, назначена ли встреча на следующие 30 минут. Если их нет, вы НЕ должны возвращать 404. Верните 200 OK и пустой массив, пожалуйста.
esmiralha
59

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

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

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

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

nategood
источник
1
Эй, я думаю, что вы можете быть на что-то. Разве не было бы больше смысла вернуть один из «41»? ошибки, когда есть проблема с запрошенным ресурсом? Например, « 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?
Кшиштоф Кубицкий
20

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

Во-первых, 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-х, не отдавая много.

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

Ральф Аллан Райс
источник
11

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

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

Джон
источник
9

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

Я строю микросервисную архитектуру с остальными 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);
}

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

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

zappee
источник
Методы Apache HttpClient не генерируют исключение для ответа 204. Если ваш клиент просто отвечает на бизнес-объекты (модели), то он вернет ноль. Работает, но сложно для конечных пользователей обнаружить ситуацию 204.
chrisinmtown
Это все хорошо, но вопрос в следующем: как часто вы пишете if заявления для 404? И что бы это было? С другой стороны, если вы делаете вызов API, который определяет возможные 404 с ошибкой json внутри, вы можете обработать это только для этого случая.
Макс
Заппее я с тобой согласен. У меня есть сервис, который возвращает данные. Но есть ситуации, когда данные не работают. URL-адрес запроса правильный (поэтому не 404), но это не совсем логическая ошибка (500), поэтому 204 кажется наиболее подходящим. Это действительно раздражает из-за отсутствия сообщения в теле сообщения. Хотя, возможно, я мог бы вставить причину в заголовок.
cs94njw
6

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

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

Майкл Даусманн
источник
1

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

судится
источник
0

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

Венката Нареш Бабу
источник