REST API 404: плохой URI или отсутствующий ресурс?

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

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

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

Рассмотрим следующий URI:
http://mywebsite/api/user/13

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

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

Ответ 1

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

Ответ 2

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

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

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

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

Ответ 3

Как и в большинстве случаев, "это зависит". Но для меня ваша практика не плоха и не противоречит спецификации 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, не отдавая много внимания.

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

Если запрашиваемый ресурс формирует часть 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 будет по-прежнему правильным (поскольку концепция USER завершает работу на 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

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

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

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

Когда не было данных для отправки обратно клиенту, я подготовил идеальное сообщение JSON с внутренним кодом ошибки и т.д., чтобы сообщить клиенту о причине "Not Found" и был отправлен обратно клиенту с HTTP 404. Это прекрасно работает.

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

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

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

Важно. Мой обработчик ошибок API останова всех исключений появляется во внутренней службе, что означает, что в случае ошибки мой API-интерфейс всегда возвращается с идеальным сообщением 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);
}

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

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

Ответ 8

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