API REST 404: поганий URI або відсутній ресурс?


219

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

Схоже, прийнята практика проектування API REST полягає в тому, що якщо запитуваний ресурс не існує, 404 повертається.

Однак мені це додає зайвої двозначності. HTTP 404 традиційно асоціюється з поганим URI. Тому насправді ми говоримо "Або ви потрапили в потрібне місце, але конкретного запису не існує, або в Інтернеті немає такого місця! Я дійсно не впевнений, який з них ..."

Розглянемо наступний URI:

http://mywebsite/api/user/13

Якщо я поверну 404, це тому, що User 13 не існує? Або це тому, що моя URL-адреса повинна була бути:

http://mywebsite/restapi/user/13

Раніше я щойно повертав результат NULL з HTTP 200 OKкодом відповіді, якщо запис не існує. Це просто, і на мій погляд, дуже чисто, навіть якщо це не обов'язково прийнята практика. Але чи є кращий спосіб це зробити?



7
Інше питання, схоже, пов'язане з форматами рядків запитів URI. Дискусія на 404 є недостатньою, щоб відповісти на моє запитання, чи є більш підходящим чи корисним способом визначення того, що насправді означає 404. Я переглянув цю перед публікацією.
Брайан Лейсі

Чи нормально, коли конколь браузера складається з помилок 404? Коли я роблю регулярні операції з правильним урі, але ресурс не знайдено.
Василь Мажекін

3
404 не вказує на поганий URI, він вказує на ресурс Not Found. Це може бути тому, що немає користувача "13", або це може бути тому, що немає ресурсу / mywebsite / api.
ChrisV

Відповіді:


101

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


7
Це має сенс. Я маю задуматися, однак, чи є якась користь насправді від повернення 404 в першу чергу, проти повернення 200 з нульовою відповіддю?
Брайан Лейсі

15
Якщо ви повернете 200 з нулем, ви переходите до специфікації HTTP. Ви можете це зробити, але тоді ваша програма не буде дотримуватися обмеження REST "Уніфікований інтерфейс".
позов

5
... і ваш орган відповіді повинен включати гіперпосилання на дійсні URI. Якщо заборонити кореневий URI і, можливо, закладку чи дві, ваші клієнти повинні завжди переходити за посиланнями, наданими їм сервером. Тоді не потрібно вигадувати детальну семантику щодо того, як саме вони вирішили працювати поза системою.
фуманчу

@DarrylHebbes, що ви маєте на увазі, я можу зробити запит і переглянути повний вміст відповіді на вкладці "Мережа" консолі розробника Chrome.
jaapz

Неймовірно погано повертати помилку (404 або будь-яку іншу) для будь-якого запиту, який повертає порожні результати. Жодна база даних цього не зробить, і зазвичай ви не хочете повідомляти про порожній результат як про несподівану умову користувачеві. Приклад: ви запитуєте api, щоб перевірити, чи запланована зустріч на наступні 30 хвилин. Якщо такої немає, НЕ повертайте 404. Поверніть 200 ОК та порожній масив, будь ласка.
esmiralha

59

Використовуйте, 404якщо ресурс не існує. Не повертайтеся 200з порожнім тілом.

Це схоже на undefinedпорожній рядок (наприклад "") у програмуванні. Незважаючи на те, що дуже схожий, є різна річ.

404означає, що в цьому URI нічого не існує (як невизначена змінна в програмуванні). Повернення 200з порожнім тілом означає, що щось там існує і що зараз щось просто порожнє (як порожній рядок у програмуванні).

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


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-х, не віддаючи багато.


1
Це рішення, яке я вибрав і використав 204 замість 404. Для мене 204 означає «я знайшов ваш код, але не знайшов результатів», а 404 означає «я не міг знайти жодного коду для виконання, це неправильний шлях? "
Метт Сандерс

11

404 Не знайдено технічно означає, що uri наразі не відображає ресурс. У вашому прикладі я інтерпретую запит, http://mywebsite/api/user/13який повертає 404, маючи на увазі, що ця URL-адреса ніколи не була відображена на ресурс. Для клієнта, це має бути кінцем розмови.

Щоб вирішити проблеми з неоднозначністю, ви можете покращити свій API, надавши інші коди відповідей. Наприклад, припустимо, що ви хочете дозволити клієнтам видавати GET запити URL http://mywebsite/api/user/13, ви хочете повідомити, що клієнти повинні використовувати канонічний URL http://mywebsite/restapi/user/13. У такому випадку ви можете розглянути можливість видалення постійного переспрямування, повернувши 301 Переміщенний Постійно та надавши канонічний URL-адресу у заголовку Location відповіді. Це говорить клієнту, що для майбутніх запитів вони повинні використовувати канонічний URL.


11

Отже, по суті, це здається, що відповідь може залежати від того, як формується запит.

Якщо запитуваний ресурс є частиною 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


9

Це дуже старий пост, але я зіткнувся з подібною проблемою, і я хотів би поділитися своїм досвідом з вами, хлопці.

Я будую мікросервісну архітектуру з API відпочинку. У мене є відпочинок GET, вони збирають дані з бек-енд-системи на основі параметрів запиту.

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

Коли не було повернено даних клієнту, я підготував ідеальне повідомлення JSON з внутрішнім кодом помилки тощо, щоб повідомити клієнта про причину "Не знайдено", і воно було відправлено назад клієнту з 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);
}

Я думаю, що це вирішує цю проблему краще.

Якщо у вас є краще рішення, будь ласка, поділіться ним з нами.


Методи Apache HttpClient не кидають винятків у відповіді 204. Якщо ваш клієнт просто відповідає на бізнес-об’єкти (моделі), то це повернеться недійсним. Працює, але кінцевим споживачам важко виявити ситуацію 204.
chrisinmtown

Це все добре, але питання: як часто ви пишете, якщо заяви 404? А що б це було? З іншого боку, якщо ви зателефонували до api, який визначає можливу 404 з помилкою json всередині, ви можете впоратися з цим лише у цьому випадку.
Макс

zappee Я згоден з вами. У мене є служба, яка повертає дані. Але є ситуації, коли дані порушуються. URL-адреса запиту правильна (тобто не 404), але це насправді не логічна помилка (500), тому 204 видається найбільш підходящим. Це дійсно дратує відсутність речі корпусу повідомлення. Хоча, мабуть, я міг би вставити причину в заголовок.
cs94njw

6

Ця стара, але відмінна стаття ... http://www.infoq.com/articles/webber-rest-workflow говорить про це ...

404 Не знайдено - Служба є занадто ледачою (або безпечною), щоб дати нам справжню причину, чому наш запит не вдався, але незалежно від причини, нам потрібно з цим розібратися.


1

Уніфікований ідентифікатор ресурсу - це унікальний вказівник на ресурс. Неправильно URI форми не вказує на ресурс, тому виконання GET на ньому не поверне ресурс. 404 означає, що сервер не знайшов нічого, що відповідає запиту-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);
     }

}

Цей блок виключень дає вам належне повідомлення, передане API REST

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.