Перейти к основному содержимому

RESTEasy клиентский API

· 4 мин. чтения

1. Введение

В предыдущей статье мы сосредоточились на реализации JAX-RS 2.0 на стороне сервера RESTEasy . ****

В JAX-RS 2.0 представлен новый клиентский API, позволяющий отправлять HTTP-запросы к удаленным веб-службам RESTful. Jersey, Apache CXF, Restlet и RESTEasy — это лишь часть наиболее популярных реализаций.

В этой статье мы рассмотрим, как использовать REST API , отправляя запросы с помощью RESTEasy API .

2. Настройка проекта

Добавьте в свой pom.xml следующие зависимости:

<properties>
<resteasy.version>4.7.2.Final</resteasy.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version}</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
...
</dependencies>

3. Клиентский код

Реализация клиента довольно маленькая и состоит из 3 основных классов:

  • Клиент

  • веб-цель

  • Ответ

Интерфейс клиента — это конструктор экземпляров WebTarget .

WebTarget представляет собой отдельный URL-адрес или шаблон URL-адреса, из которого вы можете создавать дополнительные веб-цели подресурсов или вызывать запросы.

На самом деле есть два способа создать клиента:

  • Стандартным способом является использование org.jboss.resteasy.client.ClientRequest.
  • RESTeasy Proxy Framework : с помощью класса ResteasyClientBuilder

Здесь мы сосредоточимся на RESTEasy Proxy Framework.

Вместо использования аннотаций JAX-RS для сопоставления входящего запроса с методом веб-службы RESTFul клиентская структура создает HTTP-запрос, который используется для вызова удаленной веб-службы RESTful.

Итак, давайте начнем писать интерфейс Java и использовать аннотации JAX-RS для методов и интерфейса.

3.1. Интерфейс ServicesClient _

@Path("/movies")
public interface ServicesInterface {

@GET
@Path("/getinfo")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
Movie movieByImdbId(@QueryParam("imdbId") String imdbId);

@POST
@Path("/addmovie")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
Response addMovie(Movie movie);

@PUT
@Path("/updatemovie")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
Response updateMovie(Movie movie);

@DELETE
@Path("/deletemovie")
Response deleteMovie(@QueryParam("imdbId") String imdbId);
}

3.2. Класс кино

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "movie", propOrder = { "imdbId", "title" })
public class Movie {

protected String imdbId;
protected String title;

// getters and setters
}

3.3. Создание запроса

Теперь мы создадим прокси-клиент, который мы можем использовать для использования API:

String transformerImdbId = "tt0418279";
Movie transformerMovie = new Movie("tt0418279", "Transformer 2");
UriBuilder FULL_PATH = UriBuilder.fromPath("http://127.0.0.1:8082/resteasy/rest");

ResteasyClient client = (ResteasyClient)ClientBuilder.newClient();
ResteasyWebTarget target = client.target(FULL_PATH);
ServicesInterface proxy = target.proxy(ServicesInterface.class);

// POST
Response moviesResponse = proxy.addMovie(transformerMovie);
System.out.println("HTTP code: " + moviesResponse.getStatus());
moviesResponse.close();

// GET
Movie movies = proxy.movieByImdbId(transformerImdbId);

// PUT
transformerMovie.setTitle("Transformer 4");
moviesResponse = proxy.updateMovie(transformerMovie);
moviesResponse.close();

// DELETE
moviesResponse = proxy.deleteMovie(batmanMovie.getImdbId());
moviesResponse.close();

Обратите внимание, что клиентский API RESTEasy основан на Apache HttpClient .

Также обратите внимание, что после каждой операции нам нужно закрыть ответ, прежде чем мы сможем выполнить новую операцию. Это необходимо, потому что по умолчанию у клиента есть только одно доступное HTTP-соединение.

Наконец, обратите внимание, как мы работаем с DTO напрямую — мы не имеем дело с логикой маршалинга/демаршалирования в JSON или XML и обратно ; это происходит за кулисами с использованием JAXB или Jackson , поскольку класс Movie был правильно аннотирован .

3.4. Создание запроса с пулом соединений

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

Response batmanResponse = proxy.addMovie(batmanMovie);
Response transformerResponse = proxy.addMovie(transformerMovie);

без вызова close() на batmanResponse — при выполнении второй строки будет выброшено исключение:

java.lang.IllegalStateException:
Invalid use of BasicClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.

Опять же — это просто происходит потому, что HttpClient по умолчанию , используемый RESTEasy, — это org.apache.http.impl.conn.SingleClientConnManager , что, конечно, делает доступным только одно соединение.

Теперь, чтобы обойти это ограничение, экземпляр RestEasyClient должен быть создан по-другому (с пулом соединений):

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
cm.setMaxTotal(200); // Increase max total connection to 200
cm.setDefaultMaxPerRoute(20); // Increase default max connection per route to 20
ApacheHttpClient43Engine engine = new ApacheHttpClient43Engine(httpClient);

ResteasyClient client = ((ResteasyClientBuilder) ClientBuilder.newBuilder()).httpEngine(engine).build();
ResteasyWebTarget target = client.target(FULL_PATH);
ServicesInterface proxy = target.proxy(ServicesInterface.class);

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

4. Вывод

В этом кратком руководстве мы представили RESTEasy Proxy Framework и создали с его помощью очень простой клиентский API.

Фреймворк дает нам еще несколько вспомогательных методов для настройки клиента и может быть определен как зеркальная противоположность серверным спецификациям JAX-RS.

Пример, использованный в этой статье, доступен в качестве образца проекта на GitHub .