Статьи

Декларативное связывание в Джерси 2.9 и выше

Пару недель назад Пару месяцев назад я смотрел, как собираюсь разрабатывать новый REST API для проекта Oracle Cloud. Однажды я планировал использовать декларативную инъекцию ссылок, созданную в Jersey 1.x Марком Хэдли. К сожалению, это еще не было переадресовано, поэтому я побеседовал с руководителем проекта, и я взялся за небольшую работу среднего размера по обновлению кода.

Одна из вещей, которая изменилась в новой версии, заключается в том, что в JAX-RS 2.0 есть объект Link поэтому вместо возможности вставлять только String и URI вы также можете вводить правильные атрибуты rel. Это означает, что существующие аннотации, закодированные Marc, были объединены в простой набор аннотаций как для заголовков Link, так и для внедренных свойств.

Эта функциональность теперь доступна вместе с простым примером. Оригинальная версия функции, которую я передал, имеет некоторые серьезные ограничения, которые будут описаны ниже, вам понадобится версия Джерси после версии 2.8, или вы можете создать образ 2.9-SNAPSHOT, который содержит мои изменения в настоящее время для реализации примера в этом блоге.

В этом блоге рассматривается использование этого нового API, чтобы обеспечить простое внедрение для API коллекций. Одним из распространенных шаблонов в сервисах RESTful, в частности, основанных на JSON, является наличие массива структурных связей на верхнем уровне структуры. Для целей этого блога я собираюсь следовать форме гипермедиа типа Collection + JSON .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
{ "collection" :
  {
    "version" : "1.0",
     
    "links" : [
      {"rel" : "create", "href" : "http://example.org/friends/"}
      {"rel" : "next", "href" : "http://example.org/friends/?offset=20&limit=10"}
      {"rel" : "previous", "href" : "http://example.org/friends/?offset=0&limit=10"}
    ],
    
    "items" : [
       ...
    ]
  }
}

Так что я могу вставить ссылки в следующем виде, но для ясности здесь не хватает пучка котлов. Это не самый аккуратный код; но в более позднем цикле должно быть возможно просто их несколько. Проект в настоящее время использует EL для доступа к свойствам — это дает преимущество, позволяющее записывать значения, поскольку вы можете представлять свойства. Я могу понять, что это не нравится некоторым; но я не уверен, вижу ли я какой-либо смысл в переходе на JavaScript в данный момент. Также не стоит удивляться аннотациям @Xml, я использую MOXy для генерации JSON — это не только XML.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
 
 
  @XmlTransient
  private int limit, offset; // Getters for these
 
  @XmlTransient
  private int modelLimit; // Getters for these
 
 
  @InjectLink(
            resource = ItemsResource.class,
            method = "query",
            style = Style.ABSOLUTE,
            bindings = {@Binding(name = "offset", value="${instance.offset}"),
                @Binding(name = "limit", value="${instance.limit}")
            },
            rel = "self"
  )
  @XmlElement(name="link")
  private String href;
 
  @InjectLinks({
    @InjectLink(
          resource = ItemsResource.class,
          style = Style.ABSOLUTE,
          method = "query",
          condition = "${instance.offset + instance.limit < instance.modelLimit}",
          bindings = {
            @Binding(name = "offset", value = "${instance.offset + instance.limit}"),
            @Binding(name = "limit", value = "${instance.limit}")
          },
          rel = "next"
    ),
    @InjectLink(
          resource = ItemsResource.class,
          style = Style.ABSOLUTE,
          method = "query",
          condition = "${instance.offset - instance.limit >= 0}",
          bindings = {
            @Binding(name = "offset", value = "${instance.offset - instance.limit}"),
            @Binding(name = "limit", value = "${instance.limit}")
          },
          rel = "prev"
  )})
  @XmlElement(name="link")
  @XmlElementWrapper(name = "links")
  @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
  List<Link> links;
 
  ....
}

Исходное портирование декларативного кода связывания, существующее в версии Jersey до 2.8, имело очень наивный код в отношении определения того, каким должен быть URI для конкретного ресурса, оно не могло работать с любыми ресурсами, которые не были в корне приложения. и не справится с параметрами запроса, которые так важны при работе с коллекциями.

Теоретически может быть несколько URI для определенного класса ресурсов; но этот код должен предполагать отображение 1: 1, текущая реализация содержит простой алгоритм, который использует метамодель Джерси, чтобы попытаться разработать структуру, если это не сработает, вы можете просто предоставить другую реализацию ResourceMappingContext .

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

Наконец, стоит отметить, что у модели подкачки есть свои проблемы, которые становятся очевидными, если вы рассматриваете коллекцию REST как некий массив, который можно безопасно перелистывать. Одновременные обновления вместе с отсутствием состояния означают, что клиент никогда не может быть уверен, что у него есть полная модель, и он должен ожидать, что некоторые элементы будут видны несколько раз при обновлении модели. Вместо этого следует рассмотреть схемы на основе курсора или связывания, что является еще одним хорошим напоминанием о том, почему вы всегда должны рассматривать URI как непрозрачный — серверу может понадобиться изменить его структуру в будущем. Но это совсем другой блог для другого дня …