HATEOAS: абсолютні або відносні URL-адреси?


Відповіді:


83

Існує тонка концептуальна двозначність, коли люди кажуть "відносний URI".

За визначенням RFC3986 , загальний URI містить:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

Хитра річ у тому, що коли схема та повноваження опущені, сама частина "шляху" може бути як абсолютним шляхом (починається з /), так і відносним шляхом, що не має коренів. Приклади:

  1. Абсолютний URI або повний URI:"http://example.com:8042/over/there?name=ferret"
  2. І це відносний uri, з абсолютним шляхом :/over/there
  3. І це відносний uri з відносним шляхом : hereабо ./hereабо ../hereабо тощо.

Отже, якщо запитання було "чи повинен сервер виробляти відносний шлях у спокійній відповіді", відповідь "Ні", а детальна причина доступна тут . Я думаю, що більшість людей (включаючи мене) проти "відносного URI" насправді проти "відносного шляху".

І на практиці більшість платформ MVC на стороні сервера можуть легко генерувати відносний URI з абсолютним шляхом, таким як /absolute/path/to/the/controller, і виникає питання "чи повинна серверна реалізація ставити префікс scheme://hostname:portперед абсолютним шляхом". Як питання ОП. Я не зовсім впевнений у цьому.

З одного боку, я все ще думаю, що рекомендується сервер, що повертає повний uri. Однак сервер ніколи неhostname:port повинен жорстко кодувати річ всередині вихідного коду, як це (інакше я б віддав перевагу відносному uri з абсолютним шляхом). Рішення полягає на стороні сервера, завжди отримуючи цей префікс із заголовка "Хост" запиту HTTP. Не впевнений, чи працює це в будь-яких ситуаціях.

З іншого боку, для клієнта здається не дуже клопітким об'єднати http://example.com:8042і абсолютний шлях. Зрештою, клієнт вже знає цю схему та доменне ім'я, коли надсилає запит на сервер, чи не так?

Загалом, я б сказав, рекомендую використовувати абсолютний URI, можливо, відновлення до відносного URI з абсолютним шляхом, ніколи не використовувати відносний шлях .


2
Це хороша відповідь (+1), з якою я погоджуюсь, окрім остаточного висновку. Однак у своїй відповіді я стверджую, що специфікація HTTP, наприклад , визначає "абсолютний" для посилання на абсолютний шлях , а не на повністю кваліфікований URI. Так що я НЕ згоден з вами (2) - це є абсолютною URI, але один , для якого клієнт повинен укласти протокол мережі і хост, так що це не є повною URI. І, отже, я також не погоджуюсь з вашим визначенням (1), яке є як повним URI, так і абсолютним URI.
Лоуренс Дол

Дякую за коментар. Я просто запозичую концепцію абсолютного шляху та відносного шляху з файлової системи. Різні терміни, я не бачу суттєвої різниці між вашою і моєю думкою. Ви також рекомендуєте форми 1 і 2, а ви проти форми 3, чи не так?
RayLuo

2
Практично кажучи, я за (2); Я думаю, що (1) вимагає, щоб серверний сервер мав багато знань, специфічних для HTTP (маючи на увазі деталі конкретного середовища HTTP, а не HTTP загалом), і (3), здається, вимагає занадто багато клієнта. Але моє міркування базувалось на оригінальній специфікації проекту, а приклади були змінені в пізнішій версії таким чином, що мої міркування втрачають силу.
Лоуренс Дол,

Особисто я (ще) зовсім не впевнений у тому, що HATEOAS, а тому попит на повернення URI має такий великий сенс для API. Я просто не бачу, щоб мої API керувались клієнтом у формі, подібній до перегляду веб-сайту; випадки використання, здається, дуже зумовлені функцією adhoc.
Лоуренс Дол,

@LawrenceDol У мене така ж плутанина щодо HATEOAS на початку. Зараз я розглядаю це як питання вибору. Ваші клієнти можуть використовувати adhoc-функцію для споживання вашого API напевно, але якщо вони / ви хочете, вони / ви все одно можете розробити шаблон для наслідування, так що клієнту не потрібно буде кодувати кожну точну URL-адресу. Це HATEOAS.
RayLuo,

13

Це залежить від того, хто пише код клієнта. Якщо ви пишете клієнт і сервер, це не має великої різниці. Ви будете страждати від створення URL-адрес на клієнті або на сервері.

Однак, якщо ви створюєте сервер, і ви очікуєте, що інші люди напишуть код клієнта, вони будуть любити вас набагато більше, якщо ви надасте повні URI. Вирішення відносних URI може бути дещо складним. Спочатку спосіб їх вирішення залежить від типу носія, що повертається. Html має базовий тег, Xml може мати xml: базові теги у кожному вкладеному елементі, канали Atom можуть мати базу в стрічці та іншу базу у вмісті. Якщо ви не надаєте своєму клієнтові явної інформації про базовий URI, тоді він повинен отримати базовий URI з URI запиту або, можливо, із заголовка Content-Location! І стежте за цією косою рисою. Базовий URI визначається, ігноруючи всі символи праворуч від останньої риски. Це означає, що кінцева коса риса зараз дуже важлива при вирішенні відносних URI.

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


11

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


7

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


За умови, що ви визначаєте "абсолютний" як абсолютний шлях (наприклад /xxx/yyy...), а не як повний URI (наприклад http://api.example.com/xxx/yyy...).
Лоуренс Дол

6

Використовуючи трихотомію RayLou, моя організація обрала перевагу (2). Основною причиною є уникнення атак XSS (Cross-Site Scripting). Проблема полягає в тому, що якщо зловмисник може внести власний корінь URL-адреси у відповідь, що повертається із сервера, тоді наступні запити користувачів (наприклад, запит автентифікації з іменем користувача та паролем) можуть бути перенаправлені на власний сервер зловмисника *.

Деякі порушували проблему можливості перенаправляти запити на інші сервери для балансування навантаження, але (хоча це не є моєю компетенцією) я б поклався на заклад, що є кращі способи ввімкнути балансування навантаження без необхідності явного перенаправлення клієнтів на різні господарі.

* будь ласка, дайте мені знати, якщо в цій лінії міркувань є якісь недоліки. Мета, звичайно, не запобігти всім атакам, а хоча б один шлях атаки.


Радий, що моя попередня відповідь була корисною для вашої організації. Так, я особисто також віддаю перевагу (2), він же абсолютний шлях без схеми. Однак мені цікаво ваші міркування. Як ви змусили вашого клієнта прийняти лише вашу без схем URL-адресу? Універсальний клієнт, такий як браузер, взагалі не відкине безсистемну URL-адресу. Тож я припускаю, що вам доведеться написати свій власний код на стороні клієнта для перевірки URL-адрес, перш ніж насправді слідувати за ними? Хоча це технічно здійсненно (але не обов’язково корисно), такий тип перевірки на стороні клієнта, як правило, не є частиною обговорення REST або HATEOAS.
RayLuo

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

5

Ви завжди повинні використовувати повну URL-адресу. Він діє як унікальний ідентифікатор ресурсу, оскільки всі URL-адреси повинні бути унікальними.

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


1
Ну, специфікація HTTP для заголовка Location говорить абсолютний URI. Абсолютний URI повинен містити схему (наприклад, http).
Mark Bober

Але питання полягає не в тому, як побудувати неконтекстні непрозорі ідентифікатори , а в тому, як побудувати посилання . Останній може справедливо зробити висновок "в тому самому розташуванні мережі, що і цей документ", і саме це Locationдає приклад специфікації заголовка - абсолютний URI, який не містить схеми URI або мережевого розташування сервера. Хоча посилання та ідентифікатори часто поєднуються, це не одне і те ж - у першого є контекст, у другого немає.
Лоуренс Дол

Чи можете ви надіслати посилання на частину специфікації, про яку ви говорите?
Mark Bober

Абсолютний URI визначає схему; URI, який не є абсолютним, називають відносним. URI також класифікуються залежно від того, є вони непрозорими або ієрархічними. Непрозорий URI - це абсолютний URI, специфічна для схеми частина якого не починається з косої риси ('/'). Непрозорі URI не підлягають подальшому аналізу. Деякі приклади непрозорих URI: mailto: java-net@java.sun.com news: comp.lang.java urn: isbn: 096139210x
Mark Bober

1
Гей, не турбуйся, людина. Ще одним моментом щодо цього є те, що я бачив людей, які використовують hrefs як ідентифікатори. Щоб клієнту не потрібно було реконструювати URL-адресу з якогось конфігураційного файлу та ідентифікатора, він просто знає URL-адресу і може кешувати на її основі.
Mark Bober

2

Важливим фактором, що враховує великі результати API, є додаткові накладні витрати на мережу, які включають повний URI повторно. Вірте чи ні, але gzip не цілком вирішує цю проблему (не впевнений, чому). Ми були вражені тим, скільки місця зайняв повний URI, коли в результаті були сотні посилань.


2

Одним недоліком використання абсолютних URI є те, що api не можна проксі-сервером.

Візьми назад ... неправда. Вам слід вибрати повну URL-адресу, включаючи домен.


3
Чому абсолютний URI не може використовувати ім’я хосту проксі?
Ед Саммерс,

1
На даний момент опрацьовуємо саме цю проблему. Ми хочемо, щоб усі запити спочатку проходили через своєрідний шар "балансування навантаження". Абсолютні URI для серверів безпосередньо порушать цю модель.
mag382

1
Я використовую Nginx для проксірування сайту з абсолютними URL-адресами. Він цілком здатний замінити серверну URL-адресу еквівалентною URL-адресою проксі-сервера. Зокрема, це прив'язка windyroad.artifactoryonline.com (яка має повністю кваліфіковані URL-адреси та повністю кваліфіковані перенаправлення) до repo.windyroad.com.au
Том Говард

2

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

Щодо мінусів, я бачу втрату контролю над тим, як ви можете направляти потік клієнтів між ресурсами в майбутньому (балансування навантаження, A / B тестування, ...), і я вважаю це поганою практикою щодо управління Інтернетом API. Надана вами URL-адреса більше не є непрозорою для клієнта (див. Аксіоми веб-архітектури Тіма Бернерса-Лі щодо непрозорості URI ). Зрештою, ви стаєте відповідальними за те, щоб клієнти були щасливими щодо їх творчого використання вашого API, навіть якщо це стосується лише структури вашого простору URL. Якщо вам коли-небудь доведеться дозволити чітко визначену модифікацію URL-адреси, розгляньте можливість використання шаблонів URI, як вони використовуються в мові додатків гіпертексту .

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