Веб-приложение как клиент REST API: как обрабатывать идентификаторы ресурсов

21

Несколько идей, связанных с REST, конфликтуют в моей голове, когда я пытаюсь реализовать их.

У меня есть серверная система API REST-ful с бизнес-логикой и веб-приложение с пользовательским интерфейсом. Из различных ресурсов о REST (в частности, REST на практике: гипермедиа и системная архитектура ) я знаю, что я не должен предоставлять необработанные идентификаторы своих сущностей, а скорее возвращать гиперссылки с rel="self".

Рассмотрим пример. REST API имеет ресурс, который возвращает человека:

<Person>
  <Links>
    <Link rel="self" href="http://my.rest.api/api/person/1234"/>
  </Links>
  <Pets>
    <Link rel="pet" href="http://my.rest.api/api/pet/678"/>
  </Pets>
</Person>

Проблема возникает с веб-приложением. Предположим, он возвращает страницу, которая содержит гиперссылку на браузеры:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

Что я должен положить в hrefатрибут? Как сохранить URL-адрес сущности API в веб-приложении, чтобы иметь возможность получить сущность, когда пользователь открывает целевую страницу?

Требования кажутся противоречивыми:

  1. Гиперссылка hrefдолжна вести к веб-приложению, потому что это система, в которой размещен пользовательский интерфейс.
  2. Он hrefдолжен иметь некоторый идентификатор объекта, поскольку веб-приложение должно иметь возможность обращаться к объекту при открытии целевой страницы.
  3. В упомянутой книге сказано, что веб-приложение не должно анализировать / создавать URL-адреса REST.

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

Таким образом, я не могу просто взять 1234URL-адрес ответа API, потому что как клиент RESTful я должен относиться к нему как к чему-то вроде этого http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. С другой стороны, я должен дать некоторый URL, который ведет к моему веб-приложению, и этого достаточно для того, чтобы приложение каким-то образом восстановило исходный URL API и использовало этот URL для доступа к ресурсам API.

Возможно, самый простой способ - просто использовать URL-адреса API ресурсов в качестве их строковых идентификаторов. Но URL-адреса веб-страниц http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234выглядят ужасно.

Все это кажется довольно простым для настольного приложения или одностраничного приложения javascript. Поскольку они живут непрерывно, они могут просто хранить URL-адреса в памяти вместе с объектами службы на время жизни приложения и использовать их при необходимости.

С помощью веб-приложения я могу представить несколько подходов, но все они кажутся странными:

  1. Замените хост в URL-адресах API и сохраните только результат. Огромным недостатком является то, что веб-приложению требуется обрабатывать любые URL-адреса, которые генерирует API, что означает чудовищную связь. Более того, это снова не RESTful, потому что мое веб-приложение начинает интерпретировать URL-адреса.
  2. Предоставьте необработанные идентификаторы в REST API вместе со ссылками, используйте их для создания URL-адресов веб-приложений, а затем используйте идентификаторы на сервере веб-приложений для поиска необходимых ресурсов в API. Это лучше, но повлияет на производительность сервера веб-приложений, поскольку веб-приложению придется проходить навигацию службы REST, выдавая цепочку запросов get-by-id некоторой формы для обработки любого запроса из браузера. Для несколько вложенного ресурса это может быть дорогостоящим.
  3. Сохраните все selfURL-адреса, возвращаемые API, в постоянном (DB?) Сопоставлении на сервере веб-приложений. Создайте для них несколько идентификаторов, используйте их для создания URL-адресов страниц веб-приложения и получения URL-адресов ресурсов службы REST. Т.е. я храню http://my.rest.api/pet/678URL где-то с новым ключом, скажем 3, и генерирую URL веб-страницы как http://my.web.app/pet/3. Это похоже на реализацию HTTP-кэша. Я не знаю почему, но мне это кажется странным.

Или все это означает, что RESTful API не могут служить бэкэндами для веб-приложений?

Павел Гатилов
источник
1
Непонятно, чего вы пытаетесь достичь, возможно, потому что ваше простое намерение скрыто под слоями архитектуры, которую вы накладываете друг на друга, поэтому трудно сказать, действительно ли «RESTful APIs» вам помогут. Исходя из того, что я понимаю в вашей проблеме, вариант 2 является простым и выполнимым решением. «Проблема» здесь присуща «API RESTful». RestIsJustSqlReinvented, и вы действительно столкнетесь с той же проблемой, когда попытаетесь извлечь достаточно сложный подграф из любой РСУБД. Используйте кеш или представление, оптимизированное для ваших запросов.
back2dos

Ответы:

5

Отредактировано с учетом обновлений вопроса, предыдущий ответ удален

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

Первое, что нужно определить, это то, имеет ли домашнее животное смысл без хозяина. Если у нас может быть домашнее животное без какого-либо владельца, то я бы сказал, что нам нужно какое-то уникальное свойство домашнего животного, которое мы можем использовать для ссылки на него. Я не верю, что это нарушит непосредственное раскрытие идентификатора, поскольку фактический идентификатор ресурса все еще будет скрыт в ссылке, которую клиент REST не будет анализировать. Учитывая это, наш ресурс для домашних животных может выглядеть так:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Теперь мы можем обновить имя этого питомца с Spot на Fido без необходимости связываться с какими-либо фактически идентификаторами ресурсов в приложении. Точно так же мы можем ссылаться на этого питомца в нашем графическом интерфейсе с помощью чего-то вроде:

http://example.com/GUI/pets/Spot

Если домашнее животное не имеет смысла без владельца (или домашние животные не допускаются в системе без владельца), то мы можем использовать владельца как часть «личности» домашнего животного в системе:

http://example.com/GUI/owners/John/pets/1 (первый питомец в списке для Джона)

Одно небольшое замечание: если и домашние животные, и люди могут существовать отдельно друг от друга, я бы не сделал точку входа для API ресурсом «Люди». Вместо этого я хотел бы создать более общий ресурс, который будет содержать ссылку на людей и домашних животных. Он может вернуть ресурс, который выглядит следующим образом:

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

Поэтому, зная только первую точку входа в API и не обрабатывая ни один из URL-адресов для определения системных идентификаторов, мы можем сделать что-то вроде этого:

Пользователь заходит в приложение. Клиент REST получает доступ ко всему списку доступных людям ресурсов, которые могут выглядеть следующим образом:

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

Графический интерфейс будет циклически проходить через каждый ресурс и распечатывать элемент списка для каждого человека, используя UniqueName в качестве «id»:

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

При этом он также может обработать каждую найденную ссылку с относительным значением «pet» и получить ресурс «pet», такой как:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Используя это, он может распечатать ссылку, такую ​​как:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

или

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

Если мы пойдем по первой ссылке и предположим, что наш ресурс входа имеет ссылку с отношением «домашние животные», поток управления будет выглядеть примерно так в GUI:

  1. Страница открыта и домашний питомец запрошен.
  2. Загрузите список ресурсов из точки входа API.
  3. Загрузите ресурс, который связан с термином «домашние животные».
  4. Просмотрите каждый ресурс из ответа "домашних животных" и найдите тот, который соответствует Spot.
  5. Показать информацию для спота.

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

Майк
источник
Спасибо, Майк. Я обновил свой вопрос, чтобы сделать его немного более понятным. Проблема с вашим ответом заключается в том, что я не могу согласиться с тем, что клиент REST может анализировать URL-адреса. Если это так, то он связан с URL-адресами. И это нарушает одну из основных идей REST: клиенты должны использовать rels для выбора ссылок, но не должны предполагать, что знают структуру URL. REST утверждает, что API может изменять URL-адреса по своему усмотрению при условии, что они relостаются прежними. Парсинг URL-адресов приближает нас к SOAP, а не к REST.
Павел Гатилов
Еще раз спасибо Вы описали подход, который мы использовали до сих пор. В некотором смысле, мы выставляем идентификаторы. Единственное, что мы пытаемся раскрыть натуральные идентификаторы, когда это возможно.
Павел Гатилов
6

Означает ли это, что RESTful API не могут служить бэкэндами для веб-приложений?

Я задаюсь вопросом, стоит ли проводить различие между REST API и веб-приложением. Ваш «веб - приложение» должно быть просто альтернативные (HTML) представления одних и тех же ресурсов - что сказать, я не понимаю , как и почему вы ожидаете доступа http://my.rest.api/...и , http://my.web.app/...и что они будут одновременно одинаковые и разные.

В этом случае ваш «клиент» - это браузер, который понимает HTML и JavaScript. То есть веб - приложение , на мой взгляд. Теперь вы можете не согласиться и подумать, что вы получаете доступ к указанному веб-приложению с помощью foo.com и выставляете все остальное с помощью api.foo.com - но вы должны спросить, как foo.com предоставил мне представление ресурса? «Бэкэнд» foo.com прекрасно понимает, как находить ресурсы на api.foo.com. Ваше веб-приложение просто стало прокси - ничем не отличается от того, что вы говорили с другим API (от кого-то другого) все вместе.

Таким образом, ваш вопрос можно обобщить так: «Как я могу описать ресурсы, используя мои собственные URI, которые существуют в других системах?» что тривиально, если учесть, что не клиент (HTML / JavaScript) должен понимать, как это сделать, а сервер. Если вы согласны с моим первым вызовом, то вы можете просто думать о своем веб-приложении как об отдельном REST API, который передает или делегирует другой REST API.

Поэтому, когда ваш клиент обращается к my.web.app/pets/1нему, он знает, как представить интерфейс pet, потому что либо это то, что было возвращено шаблоном на стороне сервера, либо, если это асинхронный запрос для какого-либо другого представления (например, JSON или XML), заголовок типа содержимого сообщает ему об этом. ,

Сервер, обеспечивающий это, отвечает за понимание того, что такое домашнее животное и как обнаружить домашнее животное в удаленной системе. Как это сделать, зависит только от вас - вы можете просто взять идентификатор и сгенерировать другой URI, что, по вашему мнению, неуместно, или вы можете иметь собственную базу данных, в которой хранится удаленный URI и прокси-сервер запроса. Сохранение этого URI - это хорошо, это эквивалентно закладкам. Вы будете делать все это только для того, чтобы иметь отдельное доменное имя. Честно говоря, я не знаю, зачем вам это нужно - ваши URI API REST также должны иметь возможность добавлять закладки.

Вы уже подняли большую часть этого в своем вопросе, но я чувствую, что вы сформулировали это таким образом, который действительно не признает, что это практический способ сделать то, что вы хотите сделать (основываясь на том, что я считаю произвольное ограничение - чтобы API и приложение были отдельными). Задавая вопрос о том, не могут ли REST API-интерфейсы быть бэк-эндами для веб-приложений, и предположив, что производительность может стать проблемой, я думаю, что вы сосредоточены на неправильных вещах. Это все равно что сказать, что вы не можете создать Mashup. Это все равно что сказать, что сеть не работает.

Doug
источник
Я не ожидаю, что веб-приложение будет просто представлением API. Он может иметь много различий, например, показывать несколько дочерних ресурсов вместе с некоторым корневым на одной странице. Я не хочу, чтобы URL-адреса веб-приложения содержали внутренние идентификаторы хранилища данных API, если вы подразумеваете это, говоря, что я ожидаю, что две системы будут одинаковыми. Я не беспокоюсь о производительности здесь, это не проблема. Вопрос на самом деле: «Как вставить 3 my.web.app/pets/3без разбора URL-адресов REST API»?
Павел Гатилов
Исправляя мою собственную перефразировку: «Как вставить 3 my.web.app/pets/3без разбора URL-адреса соответствующего ресурса REST API my.rest.api/v0/persons/2/pets/3? Или что я там положу?
Павел Гатилов
Я думаю, что вы путаете состояние клиента с представлениями, которые определяют это состояние. Вы не помещайте 3в app/pets/3потому app/pets/3непрозрачен, он указывает на то , что ресурс вашего веб - приложение хочет. Если это составное представление нескольких других ресурсов (в других системах - ваш API является одним из них), то вам нужно сохранить гиперссылки на эти системы на сервере веб-приложений, а затем извлечь их, преобразовать их в их представления ( например, JSON или XML), а затем подайте их как часть вашего ответа.
Даг
Подумайте об этом так - забудьте ваш API и приложение. Предположим, вы хотите создать сайт, который позволит людям собирать свои любимые посты в Facebook и Twitter. Это удаленные системы. Вы не будете пытаться туннелировать или шаблонировать URI для этих систем через свои собственные. Вы создадите ресурс 'Board', и ваш сервер будет знать, на что он board/1указывает, facebook.com/post/123и twitter.com/status/789- когда вы предоставите представление своей доски, вам придется преобразовать эти URI в представление, с которым вы можете работать. Кеш где надо.
Даг
И так, поскольку вы хотите, чтобы ваш API значительно отличался от вашего приложения (я все еще думаю, что это сомнительно) - обращение с ним, как с удаленной системой, не становится ничем иным. Вы сказали, что производительность не является проблемой, но вы также сказали в своем вопросе, что что-то подобное «повлияет на производительность».
Даг
5

Предисловие

В этом ответе конкретно рассматривается вопрос о том, как управлять своей собственной схемой URL-адресов, включая уникальные закладки для URL-адресов для ресурсов, для которых внутренний REST API не предоставляет явно идентификатор, и без интерпретации URL-адресов, предоставляемых API.


Обнаружение требует определенного количества знаний, поэтому вот мой взгляд на сценарий реального мира:

Допустим, нам нужна страница поиска, на http://my.web.app/personкоторой результаты содержат ссылку на страницу сведений для каждого человека. Одна вещь , наш передний конец кода должны знать, чтобы сделать что - нибудь вообще является базовым URL для источника данных REST: http://my.rest.api/api. Ответ на запрос GET на этот URL может быть:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

Поскольку мы намереваемся отобразить список людей, мы затем отправляем GETзапрос href по personссылке href, которая может вернуть:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

Мы хотим отображать результаты поиска, поэтому мы будем использовать службу поиска, отправив GETзапрос на searchссылку href, которая может вернуть:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

Наконец, у нас есть результаты, но как мы можем создать наши внешние URL-адреса?

Давайте отбросим часть, которую мы знаем наверняка: базовый URL API, а остальное используем в качестве идентификатора внешнего интерфейса:

  • известная база API: http://my.rest.api/api
  • данный URL для отдельной сущности: http://my.rest.api/api/person/1
  • уникальный идентификатор: /person/1
  • наш базовый URL: http://my.web.app
  • наш созданный интерфейсный URL: http://my.web.app/person/1

Наши результаты могут выглядеть так:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

Когда пользователь переходит по этой внешней ссылке на страницу сведений, по какому URL-адресу мы отправляем GETзапрос на получение сведений об этой конкретной информации person? Мы знаем наш метод сопоставления внутренних URL-адресов с внешними URL-адресами, поэтому мы просто обращаем его вспять:

  • интерфейсный URL: http://my.web.app/person/1
  • наш базовый URL: http://my.web.app
  • уникальный идентификатор: /person/1
  • известная база API: http://my.rest.api/api
  • URL сгенерированного API: http://my.rest.api/api/person/1

Если API REST изменяется таким образом, что personURL-адрес уже есть, http://my.rest.api/api/different-person-base/person/1а кто-то ранее уже добавил его в закладки http://my.web.app/person/1, API-интерфейс REST должен (хотя бы на время) обеспечить обратную совместимость, отвечая на старый URL-адрес перенаправлением на новый. Все сгенерированные внешние ссылки будут автоматически включать новую структуру.

Как вы, наверное, заметили, для навигации по API необходимо знать несколько вещей:

  • базовый URL API
  • personсоотношение
  • searchсоотношение

Я не думаю, что с этим что-то не так; мы не предполагаем конкретную структуру URL в любой момент, поэтому структура URL объекта http://my.rest.api/api/person/1может измениться, и пока API обеспечивает обратную совместимость, наш код будет работать.


Вы спросили, как наша логика маршрутизации может определить разницу между двумя интерфейсными URL-адресами:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3,

Сначала я укажу, что вы использовали базу API в своем комментарии, когда в нашем примере мы используем отдельные базовые URL для интерфейса пользователя и REST API. Я собираюсь продолжить пример, используя отдельные базы, но совместное использование базы не проблема. Мы можем (или должны быть в состоянии) отобразить методы маршрутизации пользовательского интерфейса, используя тип носителя из заголовка Accept запроса.

Что касается маршрутизации на конкретную страницу сведений, мы не можем дифференцировать эти два URL-адреса, если мы строго избегаем каких-либо знаний о структуре selfURL-адреса, предоставляемой API (т. Е. Непрозрачного идентификатора строки). Чтобы сделать это, давайте включим еще одну из наших известных частей информации - тип сущности, с которым мы работаем - в наши внешние URL-адреса.

Ранее наши внешние URL-адреса были в формате: ${UI base}/${opaque string id}

Новый формат может быть: ${UI base}/${entity type}/${opaque string id}

Таким образом, используя /person/1пример, мы в конечном итоге http://my.web.app/person/person/1.

С этим форматом будет работать наша логика маршрутизации пользовательского интерфейса /person/person/1, и, зная, что первый токен в строке был вставлен нами, мы можем извлечь его и направить на соответствующую (подробно описанную в этом примере) страницу подробностей на основе этого. Если вы чувствуете себя неловко из-за этого URL, мы могли бы добавить туда немного больше; может быть: http://my.web.app/person/detail/person/1

В этом случае мы разбираем /person/detailдля маршрутизации и используем остальное в качестве идентификатора непрозрачной строки.


Я думаю, что это вводит чрезвычайно тесную связь веб-приложения с API.

Я предполагаю, что вы имеете в виду, что, поскольку наш сгенерированный интерфейсный URL-адрес содержит часть URL-адреса API, если структура URL-адреса API изменяется без поддержки старой структуры, нам потребуется изменение кода, чтобы преобразовать URL-адрес с закладками в новая версия API URL. Другими словами, если REST API изменяет идентификатор ресурса (непрозрачную строку), то мы не можем говорить с сервером об этом ресурсе, используя старый идентификатор. Я не думаю, что мы можем избежать изменения кода в этой ситуации.

Что если бы я хотел, чтобы структура URL для веб-приложения отличалась от структуры API?

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

Что если объекты моего веб-приложения не отображаются на объекты API 1-1?

Ответ зависит от отношений. В любом случае вам потребуется способ сопоставить интерфейс с URL-адресами API.

Майк Партридж
источник
У меня есть одна проблема с этим подходом. На самом деле это номер 1 в моем списке решений. Что я не получаю, так это: если веб-приложение не интерпретирует URL-адреса и обрабатывает уникальные идентификаторы как непрозрачные строки (просто person/1, pet/3), то как оно узнает, что если браузер открывается, http://my.rest.api/api/person/1он должен отображать пользовательский интерфейс, и если открывает http://my.rest.api/api/pet/3, то пэт интерфейс?
Павел Гатилов
Хороший вопрос! Я обновил ответ своим ответом.
Майк Партридж
Спасибо, Майк. Я думаю, что это вводит чрезвычайно тесную связь веб-приложения с API. Что если бы я хотел, чтобы структура URL для веб-приложения отличалась от структуры API? Что если объекты моего веб-приложения не отображаются на объекты API 1-1? Я все еще думаю, что мне лучше воспользоваться подходом раскрытия некоторых идентификаторов, но призываем клиентов использовать ссылки для навигации.
Павел Гатилов
Это интересная тема, поэтому я надеюсь, что я ничего не пропустил. Я обновил свой ответ ответами на ваш комментарий. Я думаю, что предоставление некоторых идентификаторов является хорошим компромиссом между полной RESTfulness и удобством использования.
Майк Партридж
Моя основная задача здесь немного более практична. Я использую ASP.NET MVC для реализации веб-приложения, и из-за некоторых внутренних правил я должен определить шаблоны URL, которые поддерживает приложение. Т.е. если определено / a / {id}, то приложение будет обрабатывать / a / 1, но не / a / 1 / b / 2. Это приводит к требованию перекомпилировать веб-приложение, если URL-адреса REST API изменяются не только для сохранения URL-адресов, добавленных в закладки, но также и просто для того, чтобы веб-приложение работало при переходе из корня. Просто потому, что гиперссылки, встроенные в html-страницы, не будут работать без этого.
Павел Гатилов
2

Посмотрим правде в глаза, нет волшебного решения. Вы читали модель зрелости Ричардсона ? Он разделяет зрелость архитектуры REST на 3 уровня: ресурсы, HTTP-глаголы и средства управления гипермедиа.

Я не должен показывать необработанные идентификаторы моих сущностей, а скорее возвращать гиперссылки с rel = "self"

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

Это вопрос баланса - вы хотите пожертвовать производительностью (и сделать свой код более сложным), но получить систему, которая будет более гибкой? Или вы предпочитаете делать вещи быстрее и проще, но платите позже, когда вносите изменения в свой API / модель?

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

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

Означает ли это, что RESTful API не могут служить бэкэндами для веб-приложений?

Я думаю, это означает, что вам не нужно создавать идеальную реализацию REST. Вы можете использовать свое второе решение, или предоставить идентификаторы сущностей или, возможно, передать api url . Это нормально, пока вы понимаете последствия и компромиссы.

daramasala
источник
0

Я думаю, что если вы придерживаетесь чего-то похожего на то, что Atom Syndication Formatвы хороши

Здесь метаданные, описывающие представляемую запись, могут быть указаны с использованием дополнительных элементов / атрибутов:

  • Согласно [RFC4287] , содержит URI, который однозначно идентифицирует запись

  • Согласно [RFC4287] этот элемент является необязательным. Если он включен, он содержит URI, который клиент должен использовать для получения записи.

Это всего лишь два моих цента.

Rusev
источник
Может быть, я чего-то не понимаю, но мне кажется, что ваш ответ не объясняет, как генерировать URL-адреса веб-приложения, являющегося клиентом REST API, не так ли?
Павел Гатилов
0

Не беспокойтесь об URL, беспокойтесь о типах медиа.

Глянь сюда (в частности, третий пункт).

API-интерфейс REST должен потратить почти все свои описательные усилия на определение типов носителей, используемых для представления ресурсов и управления состоянием приложения, или на определение имен расширенных отношений и / или разметки с поддержкой гипертекста для существующих стандартных типов носителей. ,


В случае типичного веб-приложения клиентом является человек ; браузер просто агент .

Так якорный тег, как

          <a href="example.com/foo/123">click here</a>

соответствует чему-то вроде

          <link type="text/html" rel="self" href="example.com/foo/123">

URL-адрес по-прежнему непрозрачен для пользователя, все, что его волнует, это типы носителей (например, text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc ). Описательный текст, содержащийся в якоре (и в атрибуте title), - это просто способ расширить тип отношений таким образом, чтобы он был понятен людям.

Причина, по которой вы хотите, чтобы URI был непрозрачным для клиентов, заключается в том, чтобы уменьшить связывание (одна из основных целей REST). Сервер может изменить / реорганизовать URI, не влияя на клиента (при условии, что у вас есть хорошая политика кэширования - что может означать отсутствие кэширования вообще).

В итоге

Просто убедитесь, что клиент (человек или машина) заботится о типах медиа и отношениях, а не об URL, и все будет в порядке.

Родрик Чепмен
источник
Родрик, мой вопрос не о создании API, а скорее о создании веб-приложения, которое находится поверх RESTful API. Я не могу понять, как типы мультимедиа могут помочь мне создать URL для веб-приложения. Хотя типы носителей имеют решающее значение для контракта на обслуживание и возможности обнаружения.
Павел Гатилов
@PavelGatilov - Является ли клиент вашего веб-приложения человеком?
Родрик Чепмен
Да, это. И очень малоопытный.
Павел Гатилов
0

Возможно, самый простой способ - просто использовать URL-адреса API ресурсов в качестве их строковых идентификаторов. Но URL-адреса веб-страниц, такие как http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 , ужасны.

Я думаю, что вы правы, это самый простой способ. Вы можете релятивизировать URL-адреса, http://my.rest.api/apiчтобы сделать их менее уродливыми:

http://my.web.app/person/person%2F1234

Если URL, предоставленный API, не соответствует этой базе, он ухудшается до ужасной формы:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

Чтобы продвинуться дальше, просмотрите ответ от API-сервера, чтобы определить, какой тип представления вы хотите представить, и прекратите кодировать разделитель и двоеточия сегмента пути:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
ajlane
источник