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 .