Статьи

WADL в Java: Нежное введение

WADL ( язык описания веб-приложений ) — это REST, что WSDL для SOAP. Само существование этого языка вызывает много споров (см .: Нужен ли нам WADL ? И в WADL или нет в WADL ). Я могу вспомнить несколько законных вариантов использования WADL, но если вы уже здесь, вы, вероятно, не ищите еще одного обсуждения. Итак, давайте перейдем к самому WADL.

В принципе 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 xmlns="http://wadl.dev.java.net/2009/02">
    <resources base="http://example.com/api"/>
</application>

Ничего особенного здесь. Обратите внимание, что тег <resources> определяет базовый адрес API. Все именованные ресурсы, которые мы только что добавим, относятся к этому адресу. Также вы можете определить несколько тегов <resources> для описания более одного API. Итак, давайте добавим простой ресурс:

1
2
3
4
5
6
7
8
<application xmlns="http://wadl.dev.java.net/2009/02">
    <resources base="http://example.com/api">
        <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
<application xmlns="http://wadl.dev.java.net/2009/02">
    <resources base="http://example.com/api">
        <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
<application xmlns="http://wadl.dev.java.net/2009/02">
    <resources base="http://example.com/api">
        <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
  schemaLocation="http://www.w3.org/2001/xml.xsd"/>

Доступ к 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 и соседей.