This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Set the Httpclient request mechanism.

@body Requests a Body template

The @body annotation declares a request Body template with parameters that match those declared by the @param annotation in the method as follows:

interface LoginClient {
 @RequestLine("POST /")
 @Headers("Content-Type: application/json")
 // json curly braces must be escaped!
 // The JSON format requires curly braces that need to be transcoded.
 @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
 void login(@Param("user_name") String user, @Param("password") String password); }... client.login("denominator"."secret");
// {"user_name": "denominator", "password": "secret"}
Copy the code

Headers request header

Feign supports setting request headers to request APIS or request clients as follows:

Set the request header for the API

Use @headers to set the static request header
// Set the Accept request header for all methods in BaseApi
@Headers("Accept: application/json")
interface BaseApi<V> {
  // Set the Content-type request header for the put method separately
  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String, V value);
}
Copy the code
Set the request header for dynamic values
@RequestLine("POST /")
@Headers("X-Ping: {token}")
void post(@Param("token") String token);
Copy the code

Set the key and value to be dynamic headers

Dynamically determine the use of different request headers at call time

You can use @headermap annotations as follows:

// the @headermap annotation sets the request header before the other way around
@RequestLine("POST /")
void post(@HeaderMap Map<String, Object> headerMap);
Copy the code

Set the request header for Target

Sometimes we need to pass in different headers based on the endpoint in an API implementation. In this case, we can use a custom RequestInterceptor or Target.

See Request Interceptors for a custom RequestInterceptor implementation

The following is an example of setting a security Header for each Target by customizing the Target:

static class DynamicAuthTokenTarget<T> implements Target<T> {
 public DynamicAuthTokenTarget(Class
       
         clazz, UrlAndTokenProvider provider, ThreadLocal
        
          requestIdProvider)
        
       ; .@Override
 public Request apply(RequestTemplate input) {
  TokenIdAndPublicURL urlAndToken = provider.get();
  if (input.url().indexOf("http") != 0) {
   input.insert(0, urlAndToken.publicURL);
  }
  input.header("X-Auth-Token", urlAndToken.tokenId);
  input.header("X-Request-ID", requestIdProvider.get());
  returninput.request(); }}... Bank bank = Feign.builder() .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));
Copy the code
  • The implementation of this method depends on a custom RequestInterceptor or Target set for the Feign client. Can be used to set headers for all API requests from a client. For example, it is used to set authentication information in the header. These methods are executed when the thread executes the API request, so they allow the header to be set dynamically at run time based on the context.

  • For example, you can set different headers for different threads based on thread-local storage.

Base APIS

Some request methods are generic, but may have different parameter types or return types.

/ / generic API
interface BaseAPI {
 @RequestLine("GET /health")
  String health(a);
 @RequestLine("GET /all")
  List<Entity> all(a);
}
// Inherit the generic API
interface CustomAPI extends BaseAPI {
 @RequestLine("GET /custom")
 String custom(a);
}
// All types have the same representation and define a unified API
@Headers("Accept: application/json")
interface BaseApi<V> {
 @RequestLine("GET /api/{key}")
 V get(@Param("key") String key);
 @RequestLine("GET /api")
 List<V> list(a);
 @Headers("Content-Type: application/json")
 @RequestLine("PUT /api/{key}")
 void put(@Param("key") String key, V value);
}
// Inherit according to different types
interface FooApi extends BaseApi<Foo> {}interface BarApi extends BaseApi<Bar> {}Copy the code

Logging

You can set up a Logger to log HTTP messages as follows:

GitHub github = Feign.builder()
decoder(new GsonDecoder())
           .logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
           .logLevel(Logger.Level.FULL)
           .target(GitHub.class, https://api.github.com);
Copy the code

Request Interceptors

When you want to modify all requests, you can use the Request Interceptors. For example, if you’re acting as an intermediary, you might want to set each request to X-Forwarded-For

static class ForwardedForInterceptor implements RequestInterceptor {
 @Override public void apply(RequestTemplate template) {
    template.header("X-Forwarded-For"."origin.host.com"); }}... Bank bank = Feign.builder() .decoder(accountDecoder) .requestInterceptor(new ForwardedForInterceptor())
         .target(Bank.class, https://api.examplebank.com);
Copy the code

Alternatively, you may need to implement Basic Auth, which has a built-in base verification interceptor

BasicAuthRequestInterceptor
Bank bank = Feign.builder()
         .decoder(accountDecoder)
         .requestInterceptor(new BasicAuthRequestInterceptor(username, password))
         .target(Bank.class, https://api.examplebank.com);
Copy the code

@Param Expansion

The @param annotation defaults to the values of the object’s toString() method. By declaring a custom param.expander, the user can control its behavior, such as formatting values of type Date:

// You can define values of type Date by setting @param's expander to datetomillis. class
@RequestLine("GET /? since={date}")
Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
Copy the code

Dynamic Query Parameters

Dynamic query parameter support, using @queryMap allows request parameters to be passed in dynamically, as follows:

@RequestLine("GET /find")
V find(@QueryMap Map<String, Object> queryMap);
Copy the code

Custom annotation scan dynamically generates clients

Native Feign can only parse one interface at a time to generate the corresponding request proxy object, which is cumbersome to parse multiple times if there are multiple calling interfaces in a package.

Extend the BeanFactoryPostProcessor interface,

Custom annotations: During interface scanning, you can pass a custom annotation that distinguishes the Feign interface and specifies the service Url to invoke

Implementing extension containers
@Component
public class FeignClientRegister implements BeanFactoryPostProcessor{
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        List<String> classes = scan(scanPath);
        if(classes==null) {return ;
        }
        Feign.Builder builder = getFeignBuilder();
        if(classes.size()>0) {for(String claz : classes) { Class<? > targetClass =null;
                try {
                    targetClass = Class.forName(claz);
                    String url=targetClass.getAnnotation(FeignApi.class).serviceUrl();
                    if(url.indexOf("http://")! =0){
                        url="http://"+url;
                    }
                    Object target = builder.target(targetClass, url);
                    beanFactory.registerSingleton(targetClass.getName(), target);
                } catch (Exception e) {
                    throw newRuntimeException(e.getMessage()); }}}}public Feign.Builder getFeignBuilder(a){
        Feign.Builder builder = Feign.builder()
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .options(new Request.Options(1000.3500))
                .retryer(new Retryer.Default(5000.5000.3));
        return builder;
    }
    public List<String> scan(String path){
        ScanResult result = newFastClasspathScanner(path).matchClassesWithAnnotation(FeignApi.class, (Class<? > aClass) -> { }).scan();if(result! =null) {return result.getNamesOfAllInterfaceClasses();
        }
        return  null; }}Copy the code