RESTful API дизайн, HATEOAS и поиск ресурсов - PullRequest
18 голосов
/ 01 февраля 2012

Одна из основных идей HATEOAS заключается в том, что клиенты должны иметь возможность начинать с единого URL-адреса точки входа и обнаруживать все доступные ресурсы и переходы состояний, доступные для них. Хотя я прекрасно понимаю, как это работает с HTML и человеком за браузером, который щелкает ссылки и кнопки «Отправить», меня интересует, как этот принцип можно применить к проблемам, с которыми мне (не) повезло иметь дело.

Мне нравится, как принцип дизайна RESTful представлен в статьях и учебных статьях, где все это имеет смысл, Как получить чашку кофе - хороший пример такого. Я постараюсь следовать соглашению и придумаю пример, который прост и лишен утомительных деталей. Давайте посмотрим на почтовые индексы и города.

Задача 1

Допустим, я хочу создать RESTful API для поиска городов по почтовым индексам. Я придумываю ресурсы под названием «города», вложенные в почтовые индексы, так что GET на http://api.addressbook.com/zip_codes/02125/cities возвращает документ, содержащий, скажем, две записи, которые представляют Дорчестер и Бостон.

Мой вопрос: как такой URL можно обнаружить через HATEOAS? Вероятно, нецелесообразно выставлять индекс всех ~ 40K почтовых индексов под http://api.addressbook.com/zip_codes. Даже если нет проблем с индексом элементов 40K, помните, что я составил этот пример, и есть коллекции гораздо большей величины.

Так что, по сути, я хотел бы представить не ссылку, а шаблон ссылки, скорее, так: http://api.addressbook.com/zip_codes/{:zip_code}/cities, и это идет вразрез с принципами и опирается на внеполосные знания, которыми обладает клиент.

Задача 2

Допустим, я хочу предоставить индекс городов с определенными возможностями фильтрации:

Конечно, эти два фильтра можно использовать вместе: http://api.addressbook.com/cities?name=X&min_population=Y.

Здесь я хотел бы представить не только URL, но также эти два возможных варианта запроса и тот факт, что они могут быть объединены. Это кажется просто невозможным без внепланового знания клиентом семантики этих фильтров и принципов объединения их в динамические URL.

Так как принципы, лежащие в основе HATEOAS, могут помочь сделать такой простой API действительно RESTful?

Ответы [ 5 ]

4 голосов
/ 01 февраля 2012

Я предлагаю использовать формы XHTML:

GET /

HTTP/1.1 OK

<form method="get" action="/zip_code_search" rel="http://api.addressbook.com/rels/zip_code_search">
   <p>Zip code search</p>
   <input name="zip_code"/>
</form>

GET /zip_code_search?zip_code=02125

HTTP/1.1 303 See Other
Location: /zip_code/02125

В HTML отсутствует атрибут rel для form.

Ознакомьтесь с этой статьей :

Подводя итог, существует несколько причин, по которым XHTML следует рассматривать как представление по умолчанию для ваших служб RESTful.Во-первых, вы можете использовать синтаксис и семантику для важных элементов, таких как <a>, <form> и <input>, вместо того, чтобы придумывать свои собственные.Во-вторых, вы получите службы, которые очень похожи на сайты, потому что их будут просматривать как пользователи, так и приложения.XHTML по-прежнему интерпретируется человеком - он просто программист во время разработки, а не пользователь во время выполнения.Это упрощает процесс разработки и помогает потребителям узнать, как работает ваш сервис.И, наконец, вы можете использовать стандартные среды веб-разработки для создания своих служб RESTful.

Также ознакомьтесь с OpenSearch .

Чтобы уменьшить количество запросов, рассмотрите этот ответ:

HTTP/1.1 200 OK
Content-Location: /zip_code/02125

<html>
<head>
<link href="/zip_code/02125/cities" rel="related http://api.addressbook.com/rels/zip_code/cities"/>
</head>
...
</html>
2 голосов
/ 18 апреля 2012

Отвечая на вопрос 1, я предполагаю, что ваша единственная точка входа - http://api.addressbook.com/zip_codes, и цель состоит в том, чтобы позволить клиенту обойти всю коллекцию почтовых индексов и в конечном итоге получить города, связанные с ними.

В этом случае я бы заставил ресурс http://api.addressbook.com/zip_codes вернуть перенаправление на первую страницу почтовых индексов, например:

http://api.addressbook.com/zip_codes?start=0&end=xxxx

Это будет содержать «страницу» ссылок на почтовые индексы (любой номер, подходящий для системы, а также ссылку на следующую страницу (и предыдущую страницу, если она есть).

Это позволит клиенту сканировать весь список почтовых индексов, если он того пожелает.

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

http://api.addressbook.com/zip_codes/02125

И тогда будет вопрос о том, включать ли информацию о городе в представление, возвращаемое URL-адресом почтового индекса, или ссылку на него в зависимости от необходимости.

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

2 голосов
/ 01 февраля 2012

Это решение приходит на ум, но я не уверен, что я бы на самом деле рекомендовал его: вместо возврата URL ресурса, вернуть URL WADL, который описывает конечную точку. Пример:

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <grammars/>
  <resources base="http://localhost:8080/cities">
    <resource path="/">
      <method name="GET">
        <request>
          <param name="name" style="query" type="xs:string"/>
          <param name="min-population" style="query" type="xs:int"/>
        </request>
        <response>
          <representation mediaType="application/octet-stream"/>
        </response>
      </method>
    </resource>
  </resources>
</application>

Этот пример был автоматически сгенерирован CXF из этого кода Java:

import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

public class Cities {
    @GET
    public Response get(@QueryParam("name") String name, @QueryParam("min-population") int min_poulation) {
        // TODO: build the real response
        return Response.ok().build();
    }
}
1 голос
/ 12 июня 2012

Я сталкивался с этими же вопросами - поэтому я рассмотрел практический пример, который решает обе эти проблемы (и некоторые из них, о которых вы еще не думали). http://thereisnorightway.blogspot.com/2012/05/api-example-using-rest.html?m=1

По сути, решение проблемы 1 состоит в том, что вы меняете свое представление (как говорит Рой, тратите свое время на ресурс). Вам не нужно возвращать все почтовые индексы, просто сделайте так, чтобы ваш ресурс содержал подкачку. Например, когда вы запрашиваете новостные страницы с новостного сайта - он дает вам сегодняшние новости и ссылки на другие, даже если все статьи могут жить под одной и той же структурой URL, т.е. ... статья / 123 и т. д.

Проблема 2 немного странная - в http есть небольшая используемая команда под названием OPTIONS, которую я использовал в примере, чтобы в основном отразить возможность url - хотя вы могли бы решить это и в представлении, это было бы просто более сложно. По сути, он возвращает пользовательскую структуру, которая показывает возможности ресурса (включая необязательные параметры).

Дайте мне знать, что вы думаете!

0 голосов
/ 02 февраля 2012

Мне кажется, вы пропустили URL-адрес закладки.Это первый URL, а не те, которые получают города или почтовые индексы.

Итак, вы начинаете с ab: = http://api.addressbook.com

Эта первая ссылка возвращает список доступных ссылок.Вот как работает веб.Вы переходите на www.yahoo.com и затем начинаете нажимать на ссылки, не зная, куда они идут.

Итак, из исходной ссылки ab: вы вернетесь к другим ссылкам, и у них могут быть ссылки REL, объясняющие, какресурсы должны быть доступны или какие параметры могут быть представлены.

Первое, что мы подумали при проектировании наших систем, - это начнем со страницы закладок и определим все различные ссылки, к которым можно получить доступ.

Я согласен с вами по поводу «внешнегознание семантики этих фильтров - мне трудно купить, что машина может просто адаптироваться к тому, что там есть, если у нее нет какой-то заранее заданной спецификации, такой как HTML.Скорее всего, клиент создан разработчиком, который знает все возможности и затем кодирует приложение, чтобы «потенциально» ожидать, что эти ссылки будут доступны.Если ссылка доступна, тогда программа может использовать логику, которую разработчик реализовал перед использованием ресурса.Если его там нет, он просто не выполняет ссылку.В конце возможные пути выкладываются до начала прохождения приложения.

...