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 Facade
для регистрации запросов.
Чтобы на самом деле получить некоторый вывод журнала, нам понадобится наша любимая реализация регистратора с поддержкой 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 не поддерживает функции гипермедиа !
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"
}
]
}
]
Мы также можем запросить отдельный ресурс книги
, добавив ISBN к запросу на получение:
curl http://localhost:8081/api/books/1447264533
5. Притвориться клиентом
Наконец, давайте определим наш клиент 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.