В принципе WADL похож на WSDL, но структура языка сильно отличается. Хотя WSDL определяет простой список сообщений и операций, которые либо потребляют, либо производят некоторые из них, WADL подчеркивает иерархическую природу веб-сервисов RESTful. В REST основным артефактом является ресурс. Каждый ресурс (существительное) представлен как URI. Каждый ресурс может определять как операции CRUD (глаголы, реализованные как методы HTTP), так и вложенные ресурсы. Вложенный ресурс тесно связан с родительским ресурсом, обычно представляющим владельца.
Простым примером будет http://example.com/api/books ресурс, представляющий список книг. Вы можете (HTTP) получить этот ресурс, то есть получить весь список. Вы также можете ПОЛУЧИТЬ http://example.com/api/books/7 ресурс, извлекая сведения о 7-й книге в разделе книг. Или вы можете даже ПОСТАВИТЬ новую версию или УДАЛИТЬ ресурс в целом, используя тот же URI. Вы не ограничены одним уровнем вложенности: GETting http://example.com/api/books/7/reviews?page=2&size=10 найдет вторую страницу (до 10 элементов) отзывов о 7-й книге. Очевидно, что вы также можете разместить другие ресурсы рядом с книгами, например, http://example.com/api/readers.
Возникло требование формально и точно описать каждый доступный ресурс, метод, запрос и ответ, как это могли сделать ребята из WSDL. WADL — это один из вариантов описания «доступных URI», хотя некоторые считают, что хорошо написанная служба REST должна быть информативной (см. HATEOAS ). Тем не менее, вот простой, пустой документ WADL:
|
1
2
3
|
</application> |
Ничего особенного здесь. Обратите внимание, что тег <resources> определяет базовый адрес API. Все именованные ресурсы, которые мы только что добавим, относятся к этому адресу. Также вы можете определить несколько тегов <resources> для описания более одного API. Итак, давайте добавим простой ресурс:
|
1
2
3
4
5
6
7
8
|
<resource path="books"> <method name="GET"/> <method name="POST"/> </resource> </resources></application> |
Это определяет ресурс в http://example.com/api/books двумя возможными способами: GET для получения всего списка и POST для создания (добавления) нового элемента. В зависимости от ваших требований вы можете также разрешить метод DELETE (чтобы удалить все элементы), и WADL несет ответственность за документирование того, что разрешено.
Помните наш пример в начале: / books / 7 ? Очевидно, что 7 является лишь примером, и мы не будем объявлять все возможные идентификаторы книг в WADL. Вместо этого есть удобный синтаксис заполнителя:
|
01
02
03
04
05
06
07
08
09
10
11
|
<resource path="books"> <method name="GET"/> <resource path="{bookId}"> <param required="true" style="template" name="bookId"/> <method name="GET"/> </resource> </resource> </resources></application> |
Следует отметить два важных аспекта: во-первых, вместо вложенного ресурса был использован заполнитель { bookId }. Во-вторых, чтобы было понятно, мы документируем этот заполнитель с помощью тега < param />. Скоро мы увидим, как его можно использовать в сочетании с методами. Просто чтобы убедиться, что вы все еще со мной, в приведенном выше документе описаны ресурсы GET / books и GET / books / some_id .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<resource path="books"> <method name="GET"/> <resource path="{bookId}"> <param required="true" style="template" name="bookId"/> <method name="GET"/> <method name="DELETE"/> <resource path="reviews"> <method name="GET"> <request> <param name="page" required="false" default="1" style="query"/> <param name="size" required="false" default="20" style="query"/> </request> </method> </resource> </resource> </resource> <resource path="readers"> <method name="GET"/> </resource> </resources></application> |
Веб-сервис становится сложным, однако он описывает довольно много операций. Прежде всего GET / books / 42 / reviews является действительной операцией. Но интересной частью является вложенный тег <request /> . Как видите, мы можем описать параметры каждого метода независимо. В нашем случае были определены необязательные параметры запроса (в отличие от параметров шаблона, использовавшихся ранее для заполнителей URI). Это дает клиенту дополнительные знания о допустимых параметрах страницы и размера запроса. Это означает, что / books / 7 / reviews? Page = 2 & size = 10 является допустимым идентификатором ресурса. И упоминал ли я, что к каждому ресурсу, методу и параметру может быть прикреплена документация в соответствии со спецификацией WADL?
Мы остановимся здесь и упомянем только об оставшихся кусочках WADL. Прежде всего, как вы уже, наверное, догадались, для каждого <method /> возможен также дочерний тег <response /> . И запрос, и ответ могут определять точную грамматику (например, в схеме XML), которой должен следовать либо запрос, либо ответ. Ответ также может документировать возможные коды ответов HTTP. Но поскольку мы будем использовать знания, которые вы приобрели до сих пор, в приложении с первым кодом, я намеренно оставил определение <grammars /> . WADL является гибким и позволяет вам определять столько информации, сколько вам нужно.
Итак, мы знаем основы WADL, теперь мы хотели бы использовать его, возможно, в качестве потребителя или производителя в приложении на основе Java. К счастью, есть описание XML-схемы wadl.xsd для самого языка, которое мы можем использовать для генерации аннотированных POJO JAXB (с помощью инструмента xjc в JDK):
|
1
2
|
$ wget http://www.w3.org/Submission/wadl/wadl.xsd$ xjc wadl.xsd |
И вот оно … висит! Жизнь разработчика программного обеспечения полна вызовов и нетривиальных проблем. А иногда это просто раздражающий сетевой фильтр, который заставляет исчезать подозрительные пакеты (вместе с полчаса вашей жизни). Нетрудно обнаружить проблему, если вспомнить ту статью, написанную примерно в 2008 году: Излишний трафик DTD W3C:
|
1
2
|
|
Доступ к xml.xsd из браузера мгновенно возвращает HTML-страницу, но инструмент xjc ждет вечно. Помогла локальная загрузка этого файла и исправление атрибута schemaLocation в wadl.xsd . Это всегда мелочи …
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
$ xjc wadl.xsd parsing a schema... compiling a schema... net/java/dev/wadl/_2009/_02/Application.java net/java/dev/wadl/_2009/_02/Doc.java net/java/dev/wadl/_2009/_02/Grammars.java net/java/dev/wadl/_2009/_02/HTTPMethods.java net/java/dev/wadl/_2009/_02/Include.java net/java/dev/wadl/_2009/_02/Link.java net/java/dev/wadl/_2009/_02/Method.java net/java/dev/wadl/_2009/_02/ObjectFactory.java net/java/dev/wadl/_2009/_02/Option.java net/java/dev/wadl/_2009/_02/Param.java net/java/dev/wadl/_2009/_02/ParamStyle.java net/java/dev/wadl/_2009/_02/Representation.java net/java/dev/wadl/_2009/_02/Request.java net/java/dev/wadl/_2009/_02/Resource.java net/java/dev/wadl/_2009/_02/ResourceType.java net/java/dev/wadl/_2009/_02/Resources.java net/java/dev/wadl/_2009/_02/Response.java net/java/dev/wadl/_2009/_02/package-info.java |
Поскольку мы будем использовать эти классы в проекте, основанном на maven (и я ненавижу фиксировать сгенерированные классы в исходный репозиторий), давайте переместим выполнение xjc в жизненный цикл maven:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxb2-maven-plugin</artifactId> <version>1.3</version> <dependencies> <dependency> <groupId>net.java.dev.jaxb2-commons</groupId> <artifactId>jaxb-fluent-api</artifactId> <version>2.0.1</version> <exclusions> <exclusion> <groupId>com.sun.xml</groupId> <artifactId>jaxb-xjc</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <executions> <execution> <goals> <goal>xjc</goal> </goals> </execution> </executions> <configuration> <arguments>-Xfluent-api</arguments> <bindingFiles>bindings.xjb</bindingFiles> <packageName>net.java.dev.wadl</packageName> </configuration></plugin> |
Что ж, pom.xml не является самым лаконичным форматом за всю историю … Неважно, это будет генерировать классы WADL XML во время каждой сборки до компиляции исходного кода. Я также люблю плагин fluent-api, который добавляет методы * () вместе с обычными сеттерами, возвращая это, чтобы разрешить цепочку. Довольно удобно. Наконец, мы определяем более приятное имя пакета для сгенерированных артефактов (если вы находите имя пакета net.java.dev.wadl._2009._02 достаточно приятным, вы можете пропустить этот шаг) и добавляете префикс Wadl ко всем сгенерированным классам файла bindings.xjb :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<jxb:bindings version="1.0" jxb:extensionBindingPrefixes="xjc"> <jxb:bindings schemaLocation="../xsd/wadl.xsd" node="/xs:schema"> <jxb:schemaBindings> <jxb:nameXmlTransform> <jxb:typeName prefix="Wadl"/> <jxb:anonymousTypeName prefix="Wadl"/> <jxb:elementName prefix="Wadl"/> </jxb:nameXmlTransform> </jxb:schemaBindings> </jxb:bindings> </jxb:bindings> |
Теперь мы готовы производить и использовать WADL в формате XML с использованием классов JAXB и POJO. Обладая этими знаниями и фундаментом, мы готовы разработать интересную библиотеку, о которой пойдет речь в следующей статье.
Ссылка: Нежное введение в WADL (на Java) от нашего партнера по JCG Томаша Нуркевича в блоге Java и соседей.