Статьи

Руководство для занятых разработчиков RESTful-сервисов на Java


В Интернете не хватает экспозиций по архитектуре REST, сервисам RESTful и их реализации на Java.
Но вот еще один. Зачем? Потому что я не мог найти что-то достаточно краткое, чтобы указать читателям
серии блогов eValhalla .


Что такое ОТДЫХ?

Акроним расшифровывается как «
Представительный государственный трансферт»., Это относится к архитектурному стилю (или шаблону), придуманному одним из основных авторов протокола HTTP. Не пытайтесь понять, что может означать фраза «передача состояния представительства». Звучит так, как будто между системами происходит некоторая передача состояния, но это немного натянуто. В основном, происходит передача ресурсов между клиентами и серверами. Клиенты инициируют запросы и получают ответные ответы. Ответы представляют собой ресурсы в некоторых стандартных типах мультимедиа, таких как XML, JSON или HTML. Но, и это важный аспект парадигмы, само взаимодействие не имеет состояния. Это серьезное архитектурное отклонение от классической клиент-серверной модели 90-х годов. В отличие от классического клиента-сервера, здесь нет понятия клиентской сессии. 


REST предлагается не как прокотол, а как архитектурная парадигма. Однако на самом деле мы в значительной степени говорим об HTTP, REST которого является абстракцией. Основными аспектами архитектуры являются (1) идентификаторы ресурсов (т.е. URI); (2) различные возможные представления ресурсов или типы интернет-медиа (например, application / json); (3) Поддержка операций CRUD для таких ресурсов, как методы HTTP GET, PUT, POST и DEL. 


Ресурсы в принципе отделены от своих идентификаторов. Это означает, что среда может доставить кэшированную версию или может каким-либо образом сбалансировать нагрузку для выполнения запроса. На практике все мы знаем, что URI на самом деле являются адресами, которые разрешаются в определенные домены, поэтому существует по крайней мере такой уровень связи. Кроме того, ресурсы отделены от их представления. Серверу может быть предложено вернуть HTML, XML или что-то еще. Идет согласование контента, когда сервер может предложить желаемое представление или нет. Операции CRUD имеют ограничения на семантику, которые могут показаться вам очевидными или нет. Операции GET, PUT и DEL требуют идентификации ресурса, в то время как POST должен создать новый ресурс.
 

Операция GET не должна иметь побочных эффектов.
Таким образом, при прочих равных условиях можно многократно вызывать GET и возвращать тот же результат. PUT обновляет ресурс, DEL удаляет его, и поэтому они оба имеют побочные эффекты, такие как POST. С другой стороны, точно так же, как GET, PUT может повторяться многократно всегда с одинаковым эффектом. На практике эта семантика примерно соблюдается. Основным исключением является метод POST, который часто используется для отправки данных на сервер для некоторой обработки, но не обязательно ожидать, что он создаст новый ресурс. 


Реализация сервисов RESTful вращается вокруг реализации этих операций CRUD для различных ресурсов.
Это может быть сделано в Java с помощью стандартного API Java, называемого JAX-RS.

ОТДЫХ на Java = 
JAX-RS = JSR 311

В мире Java, когда дело доходит до REST, у нас есть
замечательный JAX-RS. И я не саркастичен! Это одна из тех технологий, которые процесс сообщества Java на самом деле получил, в отличие от многих других ошибок. API определен как JSR 311 и имеет версию 1.1, а работа над версией 2.0 продолжается. 

Прелесть JAX-RS в том, что он почти целиком основан на аннотациях. Это означает, что вы можете превратить практически любой класс в службу RESTful. Вы можете просто превратить POJO в конечную точку REST, добавив к ней аннотации JSR 311. Такие аннотированные POJO называются
классом ресурсов в терминах JAX-RS.

Некоторые из аннотаций JAX-RS находятся на уровне класса, некоторые на уровне метода и другие на уровне параметра метода. Некоторые доступны как на уровне класса, так и на уровне метода. В конечном итоге аннотации объединяются, превращая данный метод Java в конечную точку RESTful, доступную по URL-адресу на основе HTTP. Аннотации должны указывать следующие элементы:

  • Относительный путь метода Java — это достигается с помощью аннотации @Path .
  • Что такое HTTP-глагол, т.е. какая операция CRUD выполняется — это делается путем указания одной из аннотаций @GET , @PUT , @POST или @DELETE
  • Тип носителя принят (т.е. формат представления) — аннотация @Consumes .
  • Тип возвращаемого носителя — аннотация @Produces .

Два последних являются необязательными. Если опущен, то все типы носителей считаются возможными Давайте рассмотрим простой пример и разберем его:

import javax.ws.rs.*;

@Path("/mail")
@Produces("application/json")
public class EmailService
{
    @POST
    @Path("/new")
    public String sendEmail(@FormParam("subject") String subject,
                            @FormParam("to") String to,
                            @FormParam("body") String body)  {
        return "new email sent";
    }
    
    @GET
    @Path("/new")
    public String getUnread()  {
        return "[]";
    }
   
    @DELETE
    @Path("/{id}")
    public String deleteEmail(@PathParam("id") int emailid)  {
        return "delete " + id;
    }
    
    @GET
    @Path("/export")
    @Produces("text/html")
    public String exportHtml(@QueryParam("searchString") 
                             @DefaultValue("") String search) {
        return "<table><tr>...</tr></table>";
    }
}

Класс определяет интерфейс RESTful для гипотетического почтового сервиса на основе HTTP. Путь верхнего уровня mail относится к корневому пути приложения. Путь к корневому приложению связан с javax.ws.rs.core.Application JAX-RS,   который вы расширяете, чтобы подключить к среде выполнения. Затем мы объявили с аннотацией @Produces, что все методы в этом сервисе создают JSON. Это просто класс по умолчанию, который можно переопределить для отдельных методов, как мы сделали в методе exportHtml . Метод sendMail  определяет типичный HTTP-пост, содержимое которого отправляется в виде HTML-формы. Намерение здесь будет состоять в публикации на http://myserver.com/mail/newформа для нового электронного письма, которое должно быть отправлено. Как видите, API позволяет связывать каждое отдельное поле формы с параметром метода. Обратите внимание, что у вас есть другой метод для того же пути.

Если вы выполните HTTP get в  / mail / new , вместо этого будет вызван метод Java с аннотацией @GET . Предположительно семантика get / mail / new будет заключаться в получении списка непрочитанных писем. Далее, обратите внимание , как путь deleteEmail метода parametarized целочисленным идентификатором электронной почты для удаления. Фигурные скобки указывают, что «id» на самом деле является параметром. Значение этого параметра привязано к тому, что  помечено @PathParam («id») . Таким образом, если мы удалим HTTP на http://myserver.com/mail/453, мы будем вызывать метод deleteEmail с аргументом emailid = 453. Наконец, exportHtmlМетод демонстрирует, как мы можем получить указатель на параметры запроса. Когда вы аннотируете параметр с помощью @QueryParam («x»), значение берется из параметра HTTP-запроса с именем x. @DefaultValue аннотации обеспечивают по умолчанию в случае, если параметр запроса отсутствует. Так, вызов http://myserver.org/mail/export?searchString=RESTful будет вызывать exportHtml метод с поиском параметра = «успокоительный».

Чтобы предоставить этот сервис, сначала нам нужно написать реализацию  javax.ws.rs.core.Application. Это всего лишь несколько строк:

public class MyRestApp extends javax.ws.rs.core.Application {
   public Set>Class> getClasses() {
      HashSet S = new HashSet();
      S.add(EmailService.class);
      return S;
   }
}

Как это будет подключено к вашему серверу, зависит от вашей реализации JAX-RS. Прежде чем мы покинем API, я должен упомянуть, что это еще не все. У вас есть доступ к объектам Request и Response. У вас есть аннотации для доступа к другой контекстной информации и метаданным, таким как заголовки HTTP, файлы cookie и т. Д. И вы можете обеспечить настраиваемую сериализацию и десериализацию между типами мультимедиа и объектами Java.

RESTful против веб-сервисов

Веб-сервисы (SOAP, WSDL) активно продвигались в последнее десятилетие, но они не стали такими вездесущими, как надеялись их поклонники. Виноват XML. Виновата жесткость XML-схемы в строгой типизации. Во всем виноваты огромные накладные расходы, сложность развертывания и управления веб-сервисом. Или, вините в частых кошмарах совместимости между реализациями. Причины найти несложно, и конечный результат заключается в том, что сервисы RESTful намного легче разрабатывать и использовать. Но есть и обратная сторона!


Простота сервисов RESTful означает, что у пользователя меньше рекомендаций по отображению логики приложения в API REST. Одна из проблем заключается в том, что вместо программных типов, которые есть в языках программирования, у нас есть примитивы Java и типы медиа. К счастью, JAX-RS позволяет реализовывать любые преобразования, которые мы хотим, между фактическими аргументами метода Java и тем, что отправляется по проводам. Другой проблемой является ограниченный набор операций, которые может предложить служба REST. В то время как с веб-сервисами вы определяете операцию и ее семантику так же, как в языке программирования общего назначения, с RESTful вы застряли с get, put, post и delete. Таким образом, свободный от кошмара несоответствия типов, но связан только с 4 возможных операций. Это не так плохо, как кажется, если рассматривать эти операции как абстрактные, мета-операции.

Ключевым моментом при разработке сервисов RESTful, независимо от того, раскрываете ли вы существующую логику приложения или создаете новую, является мышление с точки зрения ресурсов данных. Это не так сложно, поскольку большинство бизнес-приложений — это манипулирование данными. Во-первых, поскольку каждая вещь идентифицируется как ресурс, нужно придумать подходящую схему именования. Поскольку URI являются иерархическими, легко создать вложенную структуру, например
/ productcategory / productname / version / partno.  Во-вторых, необходимо решить, какие виды представлений должны поддерживаться как при выводе, так и при вводе. Для современного AJAX webpp мы бы в основном использовали JSON. Я бы рекомендовал JSON поверх XML даже в условиях B2B, когда серверы общаются друг с другом.

Наконец, необходимо классифицировать бизнес-операции как один из GET, PUT, POST и DELETE. Это, вероятно, немного менее интуитивно, но это просто вопрос привыкания. Например, вместо того, чтобы думать об операции «Оформить заказ», подумайте о размещении нового заказа. Вместо того, чтобы думать об операции «Логин пользователя», подумайте о получении токена аутентификации. В общем, каждая бизнес-операция каким-то образом манипулирует некоторыми данными. Поэтому каждая бизнес-операция может вписаться в эту грубую модель CRUD. Очевидно, что большинство операций только для чтения должно быть GET. Однако иногда вам приходится отправлять большой объем данных на сервер, и в этом случае вам следует использовать POST. Например, вы можете опубликовать какой-то очень трудоемкий запрос, для которого требуется много текста.Тогда ресурс, который вы создаете, является, например, результатом запроса. Другой способ решить, следует ли вам POST или нет, если у вас есть уникальный идентификатор ресурса. Если нет, то используйте POST. Очевидно, что операции, которые вызывают удаление некоторых данных, должны быть УДАЛЕНЫ. Операции, которые «хранят» данные, — PUT и снова POST. Выбор между этими двумя простыми: используйте PUT всякий раз, когда вы изменяете существующий ресурс, для которого у вас есть идентификатор. В противном случае используйте POST.используйте PUT всякий раз, когда вы изменяете существующий ресурс, для которого у вас есть идентификатор. В противном случае используйте POST.используйте PUT всякий раз, когда вы изменяете существующий ресурс, для которого у вас есть идентификатор. В противном случае используйте POST. 


Реализации и ресурсы

Есть несколько реализаций на выбор.
Поскольку я не пробовал их все, я не могу предложить конкретные комментарии. Большинство из них раньше требовали контейнеров с сервлетами. 
Restlet рамка Джерома Лувли никогда не делала, и именно поэтому мне понравилось. Его документация оставляет желать лучшего, и если вы посмотрите на его код, он в какой-то степени чрезмерно спроектирован, но тогда амбициозный фреймворк Java не таков. Еще один новичок, который строго посвящен REST и кажется легковесным, — это
Wink , инкубированный проект Apache. Я не пробовал, но выглядит многообещающе. И конечно же, не стоит забывать о ссылочной реализации
Джерси, Преимущество Джерси состоит в том, что он в любой момент времени наиболее актуален со спецификацией. Первоначально это зависело от Tomcat. В настоящее время кажется, что он может работать автономно, поэтому он на одном уровне с Restlet, который я упомянул первым, потому что это то, что я в основном использовал. 


Вот некоторые дополнительные ресурсы для чтения, пусть их репрезентативное состояние будет передано в ваш мозг и правильно закодировано из HTML / PDF в компактную и эффективную нейронную сеть:
  1. Статья Википедии  на отдыхе не в очень хорошей форме, но все же отправная точка , если вы хотите копать глубже в концептуальные рамки. 
  2. Refcard от Dzone.com:  http://refcardz.dzone.com/refcardz/rest-foundations-restful#refcard-download-social-buttons-display 
  3. Руководство пользователя Wink выглядит хорошо написанным. Поскольку это реализация JAX-RS, это хорошая документация по этой технологии.
  4. http://java.dzone.com/articles/putting-java-rest : довольно хорошее введение в JAX-RS API для демонстрации и рассказа со ссылкой на более подробное описание концепций REST тот же автор. Стоит прочитать. 
  5.  http://jcp.org/en/jsr/detail?id=311 : официальная страница JSR 311. Загрузите спецификацию и API Javadocs оттуда.
  6. http://jsr311.java.net/nonav/javadoc/index.html : онлайн-доступ к JSR 311 Javadocs.

Если вы знаете что-то лучше, что-то хорошее, пожалуйста, оставьте это в комментарии, и я включу в этот список.


PS: мне любопытно, если люди начинают новые проекты с сервлетами, JSP / JSF в эти дни?
Мне было бы любопытно, каково было бы обоснование выбора тех, кто использует сервисы AJAX + RESTful через JSON. Как я уже говорил выше, эта статья предназначена для того, чтобы помочь читателям
серии блогов eValhalla, в которой рассказывается о развитии проекта eValhalla, точно следуя модели AJAX + REST.