«1. Обзор

Парадигма REST существует уже несколько лет и до сих пор привлекает большое внимание.

RESTful API можно реализовать на Java несколькими способами: вы можете использовать Spring, JAX-RS или просто написать свои собственные голые сервлеты, если вы достаточно хороши и смелы. Все, что вам нужно, — это возможность открывать HTTP-методы — все остальное зависит от того, как вы их организуете и как вы направляете клиента при вызовах вашего API.

Как видно из названия, эта статья будет посвящена JAX-RS. Но что значит «просто API»? Это означает, что основное внимание здесь уделяется прояснению путаницы между JAX-RS и его реализациями и предложению примера того, как выглядит правильное веб-приложение JAX-RS.

2. Включение в Java EE

JAX-RS не более чем спецификация, набор интерфейсов и аннотаций, предлагаемых Java EE. И затем, конечно же, у нас есть реализации; некоторые из наиболее известных — RESTEasy и Jersey.

Кроме того, если вы когда-нибудь решите создать сервер приложений, совместимый с JEE, ребята из Oracle скажут вам, что, среди прочего, ваш сервер должен обеспечивать реализацию JAX-RS для использования развернутыми приложениями. Вот почему она называется платформой Java Enterprise Edition.

Еще одним хорошим примером спецификации и реализации являются JPA и Hibernate.

2.1. Lightweight Wars

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

Конечно, мы можем предоставить реализацию (вот учебник по RESTeasy). Но тогда мы больше не можем называть наше приложение «Java EE app». Если завтра кто-то придет и скажет: «Хорошо, пора переходить на Glassfish или Payara, JBoss стал слишком дорогим!», возможно, мы сможем это сделать, но это будет непростая работа.

Если мы предоставляем нашу собственную реализацию, мы должны убедиться, что сервер знает, что нужно исключить свою собственную — обычно это происходит, если внутри развертываемого объекта имеется проприетарный XML-файл. Само собой, такой файл должен содержать всякие теги и инструкции, о которых никто ничего не знает, кроме разработчиков, покинувших компанию три года назад.

2.2. Всегда знай свой сервер

До сих пор мы говорили, что должны использовать преимущества платформы, которую нам предлагают.

Прежде чем принять решение о том, какой сервер использовать, мы должны посмотреть, какую реализацию JAX-RS (имя, поставщик, версию и известные ошибки) он предоставляет, по крайней мере, для производственных сред. Например, Glassfish поставляется с Джерси, а Wildfly или Jboss — с RESTEasy.

Это, конечно, означает небольшие затраты времени на исследование, но это предполагается сделать только один раз, в начале проекта или при его переносе на другой сервер.

3. Пример

Если вы хотите начать играть с JAX-RS, кратчайший путь: иметь проект веб-приложения Maven со следующей зависимостью в pom.xml:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>

Мы с использованием JavaEE 7, так как уже существует множество серверов приложений, реализующих его. Этот jar API содержит аннотации, которые вам нужно использовать, расположенные в пакете javax.ws.rs. Почему область действия «предусмотрена»? Потому что этот jar-файл также не обязательно должен быть в окончательной сборке — он нужен нам во время компиляции и предоставляется сервером во время выполнения.

После добавления зависимости мы сначала должны написать класс входа: пустой класс, который расширяет javax.ws.rs.core.Application и аннотируется javax.ws.rs.ApplicationPath:

@ApplicationPath("/api")
public class RestApplication extends Application {
}

Мы определили путь входа как /api. Какие бы другие пути мы ни объявляли для наших ресурсов, они будут иметь префикс /api.

Далее давайте посмотрим на ресурс:

@Path("/notifications")
public class NotificationsResource {
    @GET
    @Path("/ping")
    public Response ping() {
        return Response.ok().entity("Service online").build();
    }

    @GET
    @Path("/get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getNotification(@PathParam("id") int id) {
        return Response.ok()
          .entity(new Notification(id, "john", "test notification"))
          .build();
    }

    @POST
    @Path("/post/")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response postNotification(Notification notification) {
        return Response.status(201).entity(notification).build();
    }
}

У нас есть простая конечная точка ping для вызова и проверки, работает ли наше приложение, GET и POST для уведомления (это просто POJO с атрибутами плюс геттеры и сеттеры).

«Разверните эту войну на любом сервере приложений, реализующем JEE7, и следующие команды будут работать:

curl http://localhost:8080/simple-jaxrs-ex/api/notifications/ping/

curl http://localhost:8080/simple-jaxrs-ex/api/notifications/get/1

curl -X POST -d '{"id":23,"text":"lorem ipsum","username":"johana"}' 
  http://localhost:8080/simple-jaxrs-ex/api/notifications/post/ 
  --header "Content-Type:application/json"

Где simple-jaxrs-ex — контекстный корень веб-приложения.

Это было протестировано с Glassfish 4.1.0 и Wildfly 9.0.1.Final. Обратите внимание, что последние две команды не будут работать с Glassfish 4.1.1 из-за этой ошибки. По-видимому, это известная проблема в этой версии Glassfish, связанная с сериализацией JSON (если вам нужно использовать эту серверную версию, вам придется самостоятельно управлять маршалингом JSON)

4. Заключение

В конце этой статьи, просто имейте в виду, что JAX-RS — это мощный API, и большинство (если не все) вещей, которые вам нужны, уже реализованы на вашем веб-сервере. Не нужно превращать развертываемый модуль в неуправляемую кучу библиотек.

Эта запись представляет собой простой пример, и все может быть сложнее. Например, вы можете написать свои собственные маршалеры. Когда это необходимо, ищите руководства, которые решают вашу проблему с JAX-RS, а не с Jersey, Resteasy или другой конкретной реализацией. Очень вероятно, что вашу проблему можно решить с помощью одной или двух аннотаций.