«1. Обзор
В этом руководстве мы познакомимся с Feign — декларативным HTTP-клиентом, разработанным Netflix.
Feign стремится упростить клиенты HTTP API. Проще говоря, разработчику нужно только объявить и аннотировать интерфейс, в то время как фактическая реализация предоставляется во время выполнения.
2. Пример
В этом руководстве мы будем использовать пример приложения книжного магазина, которое предоставляет конечную точку REST API.
Мы можем легко клонировать проект и запускать его локально:
mvn install spring-boot:run
3. Настройка
Во-первых, добавим необходимые зависимости:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.11</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>10.11</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>10.11</version>
</dependency>
Помимо зависимости feign-core (которая также вытащил), мы будем использовать несколько плагинов, в частности: feign-okhttp для внутреннего использования клиента Square OkHttp для выполнения запросов, feign-gson для использования Google GSON в качестве процессора JSON и feign-slf4j для использования фасада Simple Logging для регистрации запросов. .
Чтобы на самом деле получить некоторый вывод журнала, нам понадобится наша любимая реализация регистратора с поддержкой SLF4J в пути к классам.
Прежде чем мы приступим к созданию нашего клиентского интерфейса, сначала мы настроим модель Book для хранения данных:
public class Book {
private String isbn;
private String author;
private String title;
private String synopsis;
private String language;
// standard constructor, getters and setters
}
ПРИМЕЧАНИЕ. Обработчику JSON необходим по крайней мере «конструктор без аргументов».
Фактически, наш поставщик REST представляет собой API, управляемый гипермедиа, поэтому нам дополнительно понадобится простой класс-оболочка:
public class BookResource {
private Book book;
// standard constructor, getters and setters
}
Примечание. Мы сохраним BookResource простым, поскольку наш пример клиента Feign не t извлечь выгоду из функций гипермедиа!
4. Сторона сервера
Чтобы понять, как определить клиент Feign, мы сначала рассмотрим некоторые методы и ответы, поддерживаемые нашим поставщиком REST.
Давайте попробуем вывести список всех книг с помощью простой команды curl. Нам нужно помнить, что перед всеми вызовами стоит префикс /api, который является контекстом сервлета приложения:
curl http://localhost:8081/api/books
В результате мы получим полный репозиторий книг, представленный в виде JSON:
[
{
"book": {
"isbn": "1447264533",
"author": "Margaret Mitchell",
"title": "Gone with the Wind",
"synopsis": null,
"language": null
},
"links": [
{
"rel": "self",
"href": "http://localhost:8081/api/books/1447264533"
}
]
},
...
{
"book": {
"isbn": "0451524934",
"author": "George Orwell",
"title": "1984",
"synopsis": null,
"language": null
},
"links": [
{
"rel": "self",
"href": "http://localhost:8081/api/books/0451524934"
}
]
}
]
Мы также может запросить отдельный ресурс Book, добавив ISBN к запросу на получение:
curl http://localhost:8081/api/books/1447264533
5. Клиент Feign
Наконец, давайте определим наш клиент Feign.
Мы будем использовать аннотацию @RequestLine, чтобы указать глагол HTTP и часть пути в качестве аргумента. Параметры будут смоделированы с использованием аннотации @Param:
public interface BookClient {
@RequestLine("GET /{isbn}")
BookResource findByIsbn(@Param("isbn") String isbn);
@RequestLine("GET")
List<BookResource> findAll();
@RequestLine("POST")
@Headers("Content-Type: application/json")
void create(Book book);
}
ПРИМЕЧАНИЕ. Клиенты Feign могут использоваться только для использования текстовых API-интерфейсов HTTP, что означает, что они не могут обрабатывать двоичные данные, например. загрузки или скачивания файлов.
Вот и все! Теперь мы будем использовать Feign.builder() для настройки нашего клиента на основе интерфейса. Фактическая реализация будет предоставлена во время выполнения:
BookClient bookClient = Feign.builder()
.client(new OkHttpClient())
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger(BookClient.class))
.logLevel(Logger.Level.FULL)
.target(BookClient.class, "http://localhost:8081/api/books");
Feign поддерживает различные подключаемые модули, такие как кодировщики и декодеры JSON/XML или базовый HTTP-клиент для выполнения запросов.
6. Модульный тест
Давайте создадим три тестовых примера для тестирования нашего клиента. Обратите внимание, мы используем статический импорт для org.hamcrest.CoreMatchers.* и org.junit.Assert.*:
@Test
public void givenBookClient_shouldRunSuccessfully() throws Exception {
List<Book> books = bookClient.findAll().stream()
.map(BookResource::getBook)
.collect(Collectors.toList());
assertTrue(books.size() > 2);
}
@Test
public void givenBookClient_shouldFindOneBook() throws Exception {
Book book = bookClient.findByIsbn("0151072558").getBook();
assertThat(book.getAuthor(), containsString("Orwell"));
}
@Test
public void givenBookClient_shouldPostBook() throws Exception {
String isbn = UUID.randomUUID().toString();
Book book = new Book(isbn, "Me", "It's me!", null, null);
bookClient.create(book);
book = bookClient.findByIsbn(isbn).getBook();
assertThat(book.getAuthor(), is("Me"));
}
7. Дальнейшее чтение
Если нам нужен какой-то запасной вариант на случай недоступности сервиса , мы могли бы добавить HystrixFeign в путь к классам и создать наш клиент с помощью HystrixFeign.builder().
Ознакомьтесь с этой специальной серией руководств, чтобы узнать больше о Hystrix.
Кроме того, если мы хотим интегрировать Spring Cloud Netflix Hystrix с Feign, здесь есть специальная статья.
Более того, в наш клиент также можно добавить балансировку нагрузки и/или обнаружение сервисов на стороне клиента.
Мы могли бы добиться этого, добавив ленту в наш путь к классам и используя конструктор следующим образом:
BookClient bookClient = Feign.builder()
.client(RibbonClient.create())
.target(BookClient.class, "http://localhost:8081/api/books");
Для обнаружения службы мы должны создать нашу службу с включенным Spring Cloud Netflix Eureka. Затем просто интегрируйте Spring Cloud с Netflix Feign. В результате мы получаем балансировку нагрузки Ribbon бесплатно. Подробнее об этом можно узнать здесь.
8. Заключение
В этой статье мы объяснили, как создать декларативный HTTP-клиент с помощью Feign для использования текстовых API.
Как обычно, все примеры кода, показанные в этом руководстве, доступны на GitHub.