RESTful API. Чи повинен я повертати об’єкт, який було створено / оновлено?


36

Я розробляв веб-службу RESTful за допомогою WebApi і мені було цікаво, які відповіді та органи відповіді HTTP повертатимуться під час оновлення / створення об’єктів.

Наприклад, я можу використовувати метод POST, щоб надіслати деякий JSON до веб-сервісу, а потім створити об'єкт. Найкраще практично встановити статус HTTP у створеному (201) або нормально (200) і просто повернути повідомлення типу "Новий співробітник додано" або повернути об'єкт, який був надісланий спочатку?

Те саме стосується методу PUT. Який статус HTTP найкраще використовувати та чи потрібно мені повертати створений об’єкт або просто повідомлення? Враховуючи той факт, що користувач все одно знає, який об’єкт намагаються створити / оновити.

Будь-які думки?

Приклад:

Додати нового працівника:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

Оновіть наявного працівника:

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Відповіді:

Відповідь із створеним / оновленим об'єктом

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Відповідь лише повідомленням:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

Відповідь за допомогою просто коду статусу:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT

2
Добре запитання, але використання терміна "найкраща практика" - це табу на цьому сайті meta.programmers.stackexchange.com/questions/2442/… Ви можете просто переписати питання. meta.programmers.stackexchange.com/questions/6967/…
Snoop

3
Як трохи подальше сповіщення, чи було б корисно мати прапор у запиті, щоб (наприклад) мобільний додаток міг повернути весь об’єкт, коли він перебуває через WiFi, а лише ідентифікатор при використанні стільникових даних? Чи є заголовок, який слід використовувати для цього, щоб уникнути забруднення JSON?
Ендрю каже, що поверніть Моніку

@AndrewPiliser Цікава ідея, хоча я особисто вважаю, що найкраще вибрати один підхід і дотримуватися його. Потім, коли ваша програма розвивається або стає більш популярною, оптимізуйте її
iswinky

@AndrewPiliser ваша ідея дуже схожа на UPDATE/INSERT ... RETURNINGваріант Postgresql для SQL. Це надзвичайно зручно, тим більше, що він зберігає подання нових даних та запит на оновлену атомну версію.
белдаз

Відповіді:


31

Як і у більшості речей, це залежить. Ваш компроміс - це простота використання та розмір мережі. Клієнтам може бути дуже корисно побачити створений ресурс. Він може включати поля, заселені сервером, такі як час останнього створення. Оскільки ви, здається, включаєтеid замість цього використовуєте hateoas, клієнти, ймовірно, захочуть побачити ідентифікатор ресурсу, який вони щойно POSTвидали.

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


+1 Завдання hateoas - не дати клієнту складати ури, також може бути досягнуто, дозволяючи клієнтові заповнити надані сервером шаблони URL-адрес з конкретними ідентифікаторами. Так, клієнт "складає", але лише таким чином "заповнюйте пробіли". Хоча HATEOAS не є чистим, він досягає мети і робить роботу з об'єктами, які мають (велику) кількість "дії", урі дещо меншу, ніж чутливу до смуги пропускання, не кажучи вже про те, коли ви розміщуєте ці об'єкти у (великому) списку.
Мар'ян Венема

3
+1 на пораду "будь ласка, не створюйте довільного повідомлення"
HairOfTheDog

Чи фокусується "відсутність довільного повідомлення" на рядкових повідомленнях або на будь-якому зворотному значенні, яке не є створеним ресурсом? Я зосереджуюсь на випадках, коли ми повертаємо ідентифікатор створеного ресурсу (але не сам ресурс), і мені цікаво, куди це підходить.
Flater

12

Особисто я завжди тільки повертаюся 200 OK.

Щоб процитувати своє запитання

Враховуючи той факт, що користувач все одно знає, який об’єкт намагаються створити / оновити.

Навіщо додавати додаткову пропускну здатність (яку, можливо, доведеться заплатити), щоб сказати клієнту, що він уже знає?


1
Ось що я думав, якщо він недійсний, ви можете повернути повідомлення про перевірку, але якщо вони дійсні і створюються / оновлюються, тоді перевірте код статусу HTTP і покажіть користувачеві повідомлення, наприклад, "Hooray" на основі цього
iswinky

3
Див. Stackoverflow.com/a/827045/290182 щодо 200/ 204 No Contentщоб не плутати jQuery тощо.
beldaz

10

@iswinky Я б завжди надсилав назад корисний вантаж у випадку POST та PUT.

У випадку POST ви можете створити об'єкт із внутрішнім ідентифікатором або UUID. Тому має сенс повернути корисний вантаж.

Аналогічно у випадку PUT ви можете ігнорувати деякі поля користувача (незмінні значення, скажімо), або у випадку PATCH, дані можуть бути змінені і іншими користувачами.

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

Лише у випадку ВИДАЛЕННЯ я не надішлю назад корисний вантаж і зробить або 200 із вмістом відповіді, або 204 без вмісту відповіді.

Редагувати: Завдяки коментарям знизу я переформулюю свою відповідь. Я все ще стою, як я розробляю свої API та надсилаю відповіді, але думаю, що є сенс визначити деякі мої дизайнерські думки.

а) Коли я кажу, що я повертаю корисне навантаження, я фактично мав на увазі переслати повернути дані ресурсу, а не той самий корисний вантаж, який був у запиті. Наприклад: якщо ви надіслаєте корисну навантаження, я можу в бекенді створити інші об'єкти, такі як UUID та (можливо) часові позначки та деякі (графічні) з'єднання навіть. Я б все це повернув у відповідь (не лише корисний навантаження запиту, як є - що безглуздо).

б) Я б не надсилав відповіді у випадку, якщо корисна навантаження дуже велика. Я обговорював це в коментарях, але те, що я хотів би застерегти, це те, що я б намагався зробити все можливе, щоб розробити свої API або свої ресурси таким чином, щоб у нього не було дуже великих корисних навантажень. Я б спробував розбити ресурси на більш дрібні та керовані об'єкти таким чином, щоб кожен ресурс визначався 15-20 атрибутами JSON і не був більшим.

У випадку, якщо об’єкт дуже великий або батьківський об'єкт оновлюється, я б надіслав назад вкладені структури як hrefs.

Підсумок полягає в тому, що я б, безумовно, намагався відправити назад дані, які мають сенс для споживача / інтерфейсу обробляти негайно і робити з атомною дією API, а не потрібно йти і отримувати ще 2-5 API, щоб оновити інтерфейс користувача після публікації створення / оновлення даних.

API-сервери на сервер можуть по-іншому подумати про це. Я зосереджуюсь на API, які сприяють користувальницькій роботі.


Я бачу багато ситуацій, коли повернення всього корисного вантажу є поганою ідеєю, коли корисна навантаження велика.
beldaz

2
@beldaz повністю згоден. YMMV на основі дизайну API REST. Я, як правило, уникаю дуже великих об'єктів і розбиваю їх на ряд субресурсів / PUT. Якщо корисна навантаження дуже велика, є кращі способи зробити це, і там ви хочете зробити HATEOAS (як каже Мар'ян вище), де ви повернете посилання на об'єкт замість самого об'єкта.
кспрашу

@ksprashu: "Отже, має сенс відправити назад корисний вантаж" - я вважаю, що це погана ідея, тому що таким чином ресурс можна отримати багатьма способами: через GET, як відповідь POST, як відповідь PUT. Це означає, що клієнт отримує 3 ресурси з потенційно різною структурою. Якщо ви ніби повернете лише URI (місцезнаходження), без тіла, тоді єдиним способом отримати ресурс було б GET. Це забезпечить, щоб клієнт завжди отримував послідовну відповідь.
mentallurg

@mentallurg Я розумію, що я, можливо, не сформулював це право. Дякую, що вказали на це. Я відредагував свою відповідь. Повідомте мене, якщо це має більше сенсу.
ksprashu

Поки ви впроваджуєте послугу для домашньої роботи, це насправді не має значення. Робіть так, як вам подобається. Економте свій час.
mentallurg

9

Посилаючись на стандарти RFC посилання , ви повинні повернути 201 (створений) статус при успішному зберіганні ресурсу запиту за допомогою Post. У більшості додатків ідентифікатор ресурсу генерується самим сервером, тому добре повернути ідентифікатор створеного ресурсу. Повернення всього об’єкта - накладні витрати для запиту на повідомлення. Ідеальною практикою є повернення URL-адреси новоствореного ресурсу.

Наприклад, ви можете посилатися на наступний приклад, який зберігає Об'єкт працівника в базі даних і повертає URL-адресу новоствореного ресурсу як відповідь.

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

Ця кінцева точка спокою поверне відповідь у вигляді:

Статус 201 - СТВОРЕНО

Місце розташування заголовка → http: // localhost: 8080 / співробітники / 1


Приємно і чисто - я буду слідувати цьому відтепер і далі
Хассан Тарек

0

Я зробив би корисне навантаження в тілі повернення залежно від параметра HTTP.

Найчастіше найкраще повернути якийсь вміст назад споживачеві API, щоб уникнути зайвих поїздок (одна з причин існування GraphQL.)

Насправді, оскільки наші програми стають більш обтяженими та розповсюдженими даними, я намагаюся дотримуватися цього керівництва:

Моя настанова :

Кожен раз, коли є випадок використання, який вимагає отримати GET відразу після POST або PUT, це випадок, коли найкраще просто повернути щось у відповідь POST / PUT.

Як це робиться і який тип вмісту повертається назад із PUT або POST, це специфічно для програми. Тепер було б цікаво, якби програма могла параметризувати тип "вмісту" в тілі відповіді (чи хочемо ми лише розташування нового об'єкта, чи деяких полів, або всього об'єкта тощо)

Додаток може визначити набір параметрів, які POST / PUT може отримати, щоб керувати типом "вмісту" для повернення в тіло відповіді. Або він може кодувати якийсь запит GraphQL як параметр (я можу бачити, що це є корисним, але також стає кошмаром технічного обслуговування.)

Так чи інакше, мені здається, що:

  1. Добре (і, швидше за все, бажано) повернути щось у тілі відповідей POST / PUT.
  2. Як це робиться специфічно для додатків і майже неможливо узагальнити.
  3. Ви не хочете повертати "контекст" великого розміру за замовчуванням (шум трафіку, який перемагає всю причину відходу від POST, за яким слідує GET).

Отже, 1) зробіть це, але 2) тримайте це просто.

Ще один варіант, який я бачив, - це люди, які створюють альтернативні кінцеві точки (скажімо, / замовники для POST / PUT, які нічого не повертають у тілі, та / customer_with_details для POST / PUT для / клієнтів, але вони повертають щось у тілі відповідей.)

Я б уникнув такого підходу. Що відбувається, коли вам законно потрібно повернути різні типи вмісту? Одна кінцева точка на тип вмісту? Не масштабується і не є ремонтом.

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