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

84

При разработке веб-службы RESTful с использованием HATEOAS, каковы плюсы и минусы отображения ссылки в виде полного URL-адреса (" http: // server: port / application / customers / 1234 ") по сравнению с просто путем ("/ application / клиентов / 1234 ")?

Марк Латтон
источник

Ответы:

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 с относительным путем : hereor ./hereor ../hereor etc.

Итак, если вопрос был в том, «должен ли сервер выдавать относительный путь в спокойном ответе», ответ будет «Нет», и подробные сведения о причине доступны здесь . Я думаю, что большинство людей (включая меня) против «относительного URI» на самом деле против «относительного пути».

И на практике большая часть серверной инфраструктуры MVC может легко сгенерировать относительный URI с абсолютным путем, например /absolute/path/to/the/controller, и возникает вопрос, «должна ли реализация сервера префикс a scheme://hostname:portперед абсолютным путем». Как вопрос ОП. Я не совсем уверен в этом.

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

С другой стороны, для клиента не составляет большого труда объединить http://example.com:8042абсолютный путь и. В конце концов, клиент уже знает эту схему и доменное имя, когда отправляет запрос на сервер, верно?

В целом, я бы сказал, рекомендую использовать абсолютный URI, возможно, вернуться к относительному URI с абсолютным путем, никогда не использовать относительный путь .

RayLuo
источник
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-интерфейсы управлялись клиентом аналогично просмотру веб-сайта; варианты использования кажутся в значительной степени обусловленными специальной функцией.
Лоуренс Дол
@LawrenceDol У меня такое же замешательство насчет HATEOAS в начале. Теперь я считаю это вопросом выбора. Ваши клиенты могут использовать специальную функцию для использования вашего API, но если они / вы хотите, они / вы все равно можете разработать шаблон, которому они будут следовать, так что клиенту не нужно будет жестко кодировать каждый точный URL-адрес. Это HATEOAS.
RayLuo
13

Это зависит от того, кто пишет клиентский код. Если вы пишете клиент и сервер, это не имеет большого значения. Вы будете либо страдать от создания URL-адресов на клиенте, либо на сервере.

Однако, если вы создаете сервер и ожидаете, что другие люди будут писать клиентский код, они полюбят вас гораздо больше, если вы предоставите полные URI. Разрешение относительных URI может быть немного сложным. Во-первых, как вы их разрешите, зависит от возвращаемого типа носителя. Html имеет базовый тег, Xml может иметь теги xml: base в каждом вложенном элементе, фиды Atom могут иметь базу в фиде и другую базу в содержимом. Если вы не предоставляете своему клиенту явную информацию о базовом URI, он должен получить базовый URI из URI запроса или, возможно, из заголовка Content-Location! И берегитесь этой косой черты в конце. Базовый URI определяется путем игнорирования всех символов справа от последней косой черты. Это означает, что завершающая косая черта теперь очень важна при разрешении относительных URI.

Единственная другая проблема, требующая небольшого упоминания, - это размер документа. Если вы возвращаете большой список элементов, каждый из которых может иметь несколько ссылок, использование абсолютных URL-адресов может добавить значительное количество байтов к вашей сущности, если вы не сжимаете сущность. Это проблема производительности, и вам нужно решать, имеет ли она значение в каждом конкретном случае.

Даррел Миллер
источник
11

Единственная реальная разница, казалось бы, заключается в том, что клиентам проще использовать абсолютные URI вместо того, чтобы создавать их из относительной версии. Конечно, этой разницы было бы достаточно, чтобы заставить меня сделать абсолютную версию.

Хэнк Гей
источник
7

По мере масштабирования приложения вы можете выполнять балансировку нагрузки, переключение при отказе и т. Д. Если вы возвращаете абсолютные URI, ваши клиентские приложения будут следовать вашей развивающейся конфигурации серверов.

CyberFonic
источник
При условии, что вы определяете «абсолютный» как абсолютный путь (например /xxx/yyy...), а не как означающий полный URI (например http://api.example.com/xxx/yyy...).
Лоуренс Дол
6

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

Некоторые поднимали вопрос о возможности перенаправлять запросы на другие серверы для балансировки нагрузки, но (хотя это не моя область знаний) я бы поспорил, что есть лучшие способы включить балансировку нагрузки без необходимости явно перенаправлять клиентов на разные хосты.

* пожалуйста, дайте мне знать, есть ли какие-либо недостатки в этой аргументации. Цель, конечно, не в том, чтобы предотвратить все атаки, а в том, чтобы предотвратить хотя бы одно направление атаки.

Rahs
источник
Рад, что мой предыдущий ответ был полезен для вашей организации. Да, я лично также предпочитаю (2), иначе говоря, абсолютный путь без схемы. Однако мне любопытны ваши рассуждения. Как вы добились того, чтобы ваш клиент принимал только ваш URL без схемы? Обычный клиент, такой как браузер, вообще не будет отклонять URL без схемы. Итак, я предполагаю, что вам придется написать свой собственный код на стороне клиента для проверки URL-адресов, прежде чем фактически следовать им? Хотя это технически выполнимо (но не обязательно полезно), этот вид проверки на стороне клиента обычно не является частью обсуждения REST или HATEOAS.
RayLuo
3
Я знаю, что это старый пост, но я просто хочу указать, что «если злоумышленник может ввести свой собственный URL-адрес в ответ, возвращающийся» - это своего рода бессмысленная причина. Если они могут «вставить свой собственный URL» в правильные места в ответе, я уверен, что они могли бы с такой же легкостью просто заменить ваше имя хоста своим собственным. Так что с точки зрения безопасности я не считаю это веским аргументом.
Магнус Эрикссон
5

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

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

Марк Бобер
источник
1
Ну, в спецификации HTTP для заголовка Location указан абсолютный URI. Абсолютный URI должен содержать схему (например, http).
Марк Бобер,
Но вопрос не в том, как создавать бесконтекстные непрозрачные идентификаторы , вопрос в том, как создавать ссылки . Последний может правильно вывести «в том же сетевом местоположении, что и этот документ», и это именно то, что Locationдает пример заголовка спецификации - абсолютный URI, который не содержит схему URI или сетевое местоположение сервера. Хотя ссылки и идентификаторы часто объединяются, это не одно и то же - первые имеют контекст, а вторые - нет.
Лоуренс Дол
Можете прислать ссылку на ту часть спецификации, о которой вы говорите?
Марк Бобер,
Абсолютный URI определяет схему; URI, который не является абсолютным, называется относительным. URI также классифицируются в зависимости от того, являются ли они непрозрачными или иерархическими. Непрозрачный URI - это абсолютный URI, часть которого, зависящая от схемы, не начинается с символа косой черты ('/'). Непрозрачные URI не подлежат дальнейшему синтаксическому анализу. Вот некоторые примеры непрозрачных URI: mailto: java-net@java.sun.com новости: comp.lang.java urn: isbn: 096139210x
Марк Бобер
1
Эй, не волнуйся, парень. Еще один момент по поводу этого материала: я видел людей, использующих hrefs в качестве идентификаторов. Так что клиенту не нужно восстанавливать URL-адрес из некоторого файла конфигурации и идентификатора, он просто знает URL-адрес и может кэшировать на его основе.
Марк Бобер,
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, правильного использования заголовков кеширования, использования etags и условных запросов на клиенте, то в конечном итоге это может потребоваться, но я ожидаю гораздо более высокой отдачи от ваши усилия были другими.

Что касается минусов, я вижу потерю контроля в отношении того, как вы можете направлять поток клиентов между ресурсами в будущем (балансировка нагрузки, A / B-тестирование, ...), и я бы счел это плохой практикой в ​​отношении управления сетью. API. Предоставляемый вами URL-адрес больше не является непрозрачным для клиента (см. Аксиомы веб-архитектуры Тима Бернерса-Ли о непрозрачности URI ). В конце концов, вы берете на себя ответственность за то, чтобы клиенты были довольны своим творческим использованием вашего API, даже если это касается только структуры вашего URL-пространства. Если вам когда-нибудь понадобится разрешить явно определенную модификацию URL-адреса, рассмотрите возможность использования шаблонов URI, используемых в языке гипертекстовых приложений .

Майкл Хартл
источник