Feign is a very simple Java library for HTTP clients.
Evaluation of: star: 2745 on github (as of April 2, 2018), so it seems to be a fairly used library.
You can make an HTTP request with very short code. The flow when using Feign
is as follows.
The disadvantages are that only text-based HTTP requests can be issued, and HTTP responses can only access BODY.
(Added on 2018/4/8)
The default of Feign
is not supported, but you can handle binary HTTP requests with another module or your own implementation.
Fegin
is configured so that the components used can be replaced.
This time, we will use ʻOkHttp,
Jackson, and
Logback`.
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>9.5.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.5.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>9.5.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Define the API you want to call as an interface.
@RequestLine
{variable name}
@Param
to the method argument@Headers
This time, I would like to consider the case of calling the API shown below.
Item number | path | HTTP method | Description |
---|---|---|---|
1 | /todos | GET | Get all TODO |
2 | /todos/{todoId} | GET | Gets the TODO specified by todoId |
3 | /todos | POST | Create a TODO with the sent data The content type is application/json To |
4 | /todos/{todoId} | PUT | Update the TODO specified by todoId The content type is application/json To |
5 | /todos/{todoId} | DELETE | Delete the TODO specified by todoId |
The API endpoint (URL) is http: // localhost: 8090 / todo-rest / api / v1
.
TodoApi.java
package com.example.feign.demo;
import java.util.List;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
public interface TodoApi {
@RequestLine("GET /todos")
List<Todo> findAll();
@RequestLine("GET /todos/{todoId}")
Todo getTodo(@Param("todoId") String todoId);
@RequestLine("POST /todos")
@Headers("Content-Type: application/json")
Todo createTodo(Todo todo);
@RequestLine("PUT /todos/{todoId}")
@Headers("Content-Type: application/json")
Todo updateTodo(@Param("todoId") String todoId, Todo todo);
@RequestLine("DELETE /todos/{todoId}")
void deleteTodo(@Param("todoId") String todoId);
}
I have defined the API interface, but not its implementation class. An instance of the API is created with Feign
.
Calling the API simply executes the methods of the interface.
TodoApiDemo.java
package com.example.feign.demo;
import java.util.List;
import feign.Feign;
import feign.Logger;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import feign.slf4j.Slf4jLogger;
public class TodoApiDemo {
public static void main(String[] args) {
// 1. create instance of api interface with feign
TodoApi todoApi = Feign.builder() // builder call at first
.client(new OkHttpClient()) // use OkHttpClient of feign
.encoder(new JacksonEncoder()) // use Jackson of feign
.decoder(new JacksonDecoder()) // use Jackson of feign
.logger(new Slf4jLogger()) // use Slf4j of feign
.logLevel(Logger.Level.FULL) // setting log level to most detail
.target(TodoApi.class,
"http://localhost:8090/todo-rest/api/v1");
// 2. call api [GET /todos]
List<Todo> todos = todoApi.findAll();
System.out.println(todos);
}
}
A dedicated method for configuration is provided to create an instance with the Builder pattern.
client
method, ʻOkHttpClient
, etc.JacksonEncoder
, GsonEncoder
, etc.decoder
methodJacksonDecoder
, GsonDecoder
, etc.logger
methodFeign
uses SLF4J
for log output, the implementation of log output can be replaced if it supports SLF4J
.logLevel
methodLogger.Level.FULL
target
methodclass
of the API interface defined in [3. Define API interface](# 3-Define API interface)It's not difficult to call it because it only calls the method of the API instance, but for reference, I will describe the test of the sample API.
Since the purpose is to use Feign
, the content of the test itself is appropriate.
TodoApiTest.java
package com.example.feign.demo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import feign.Feign;
import feign.Logger;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import feign.slf4j.Slf4jLogger;
public class TodoApiTest {
// test api
TodoApi todoApi;
private <T> T factory(Class<T> apiType, String url) {
return Feign.builder() // builder call at first
.client(new OkHttpClient()) // use OkHttpClient of feign
.encoder(new JacksonEncoder()) // use Jackson of feign
.decoder(new JacksonDecoder()) // use Jackson of feign
.logger(new Slf4jLogger()) // use Slf4j
.logLevel(Logger.Level.FULL) // setting log level to most detail
.target(apiType, url);
}
@Before
public void setUp() throws Exception {
todoApi = factory(TodoApi.class,
"http://localhost:8090/todo-rest/api/v1");
}
@Test
public void testFindAll() {
List<Todo> todos = todoApi.findAll();
System.out.println(todos);
assertThat(todos.size(), is(2));
}
@Test
public void testCreateTodo() {
Todo todo = new Todo();
todo.setTodoTitle("hello");
Todo registeredTodo = todoApi.createTodo(todo);
System.out.println(registeredTodo);
}
@Test
public void testGetTodo() {
String todoId = "36e06987-ef33-436a-a2c6-d215096ee902";
Todo todo = todoApi.getTodo(todoId);
System.out.println(todo);
assertThat(todo.getTodoId(), is(todoId));
}
@Test
public void testUpdateTodo() {
String todoId = "36e06987-ef33-436a-a2c6-d215096ee902";
String title = "update......";
Todo todo = new Todo();
todo.setTodoId(todoId);
todo.setTodoTitle(title);
todo.setCreatedAt(new Date());
Todo updatedTodo = todoApi.updateTodo(todoId, todo);
System.out.println(updatedTodo);
assertThat(updatedTodo.getTodoId(), is(todoId));
assertThat(updatedTodo.getTodoTitle(), is(title));
assertThat(updatedTodo.isFinished(), is(true));
}
@Test
public void testDeleteTodo() {
String todoId = "36e06987-ef33-436a-a2c6-d215096ee902";
todoApi.deleteTodo(todoId);
System.out.println("ok");
}
}
This time, I explained about the Java HTTP client library Feign
.
It's a very easy-to-use library because it's almost done by defining the API interface.
Although not explained this time, Feign
also supports JAXB
and JAX-RS
.
There is a restriction that only text-based HTTP requests can be handled, but I wanted to actively use it with APIs that are not affected by it.