В принципе 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 и соседей.