Feign-学习分享-1 入门

feign 简介

  • Feign是声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API

  • Feign 在英文中是“假装,伪装”的意思,它是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。

    • Feign通过处理注解,将请求模板化
    • 当实际调用的时候,传入参数
    • 根据参数再应用到请求上,进而转化成真正的请求
  • Feign通过可定制的解码器和错误处理将您的代码与http API连接起来,并且只需要很少的开销。

    开源项目地址: https://github.com/OpenFeign/feign

    使用场景

在服务调用的场景中,我们经常调用基于Http协议的服务,而我们经常使用到的框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty等等,这些框架在基于自身的专注点提供了自身特性。而从角色划分上来看,他们的职能是一致的提供Http调用服务。大致流程如下:
在这里插入图片描述

入门示例

DEMO

典型的用法如下:

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

}

public static class Contributor {
  String login;
  int contributions;
}

public class MyApp {
  public static void main(String... args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
  
    // Fetch and print a list of the contributors to this library.
    List<Contributor> contributors = github.contributors("Wager-Q", "wager");
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}`

注解介绍

Feign注解定义了接口和底层客户端如何工作之间的契约(Contract)。Feign的默认合同定义了以下注释:

Annotation Interface Target Usage
@RequestLine Method 为请求定义HttpMethod和UriTemplate。表达式、用大括号括起来的值{expression}使用对应的@Param带注释的参数解析
@Param Parameter 通过名称定义一个模板变量,该变量的值将用于解析相应的模板表达式。
@Headers Method, Type 定义了一个HeaderTemplate;UriTemplate。它使用@Param注释的值来解析相应的表达式。当用于类型时,模板将应用于每个请求。在方法上使用时,模板将仅应用于带注释的方法。
@QueryMap Parameter 定义名称-值对(POJO)的映射,以展开成查询字符串。
@HeaderMap Parameter 定义名称-值对的映射,扩展为Http标头
@Body Method 定义一个模板,类似于UriTemplate和HeaderTemplate,它使用@Param注释的值来解析相应的表达式

工作流程

1586144561102.png

各个模块功能分析


Headers

静态标头可以使用’ @Headers '注释在api接口或方法上设置。

扫描二维码关注公众号,回复: 10543415 查看本文章
@Headers("Accept: application/json")
interface BaseApi<V> {
  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String key, V value);
}

方法可以使用’ @Headers '中的变量展开为静态标头指定动态内容。

public interface Api {
   @RequestLine("POST /")
   @Headers("X-Ping: {token}")
   void post(@Param("token") String token);
}

如果header键和值都是动态的, 可以使用’ HeaderMap '注释映射参数,以构造一个使用映射的内容作为其头部参数的查询。

public interface Api {
   @RequestLine("POST /")
   void post(@HeaderMap Map<String, Object> headerMap);
}

Body templates

Body注解可以使用@Param注释的参数填充模板中的参数。

interface LoginClient {

  @RequestLine("POST /")
  @Headers("Content-Type: application/xml")
  @Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
  void xml(@Param("user_name") String user, @Param("password") String password);

  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  // json curly braces must be escaped!
  @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
  void json(@Param("user_name") String user, @Param("password") String password);
}

public class Example {
  public static void main(String[] args) {
    client.xml("denominator", "secret"); // <login "user_name"="denominator" "password"="secret"/>
    client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"}
  }
}

Encoders

  • 将请求体发送到服务器的最简单方法是定义一个POST方法,该方法有一个String或byte[]参数,上面没有任何注解。可能需要添加一个Content-Type头。
interface LoginClient {
  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  void login(String content);
}

public class Example {
  public static void main(String[] args) {
    client.login("{\"user_name\": \"denominator\", \"password\": \"secret\"}");
  }
}

通过配置编码器,可以发送类型安全的请求体。下面是一个使用 feign-gson 的例子 :

static class Credentials {
  final String user_name;
  final String password;

  Credentials(String user_name, String password) {
    this.user_name = user_name;
    this.password = password;
  }
}

interface LoginClient {
  @RequestLine("POST /")
  void login(Credentials creds);
}

public class Example {
  public static void main(String[] args) {
    LoginClient client = Feign.builder()
                              .encoder(new GsonEncoder())
                              .target(LoginClient.class, "https://foo.com");
    
    client.login(new Credentials("denominator", "secret"));
  }
}

Decoders

如果接口中除了Response、String、byte[]或void之外还有其他方法返回类型,则需要配置一个非默认解码器。

下面是如何配置JSON解码:

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
  }
}

Error Handling

如果您需要更多的控制来处理意外的响应,Feign实例可以通过构建器注册一个自定义的“ErrorDecoder”。

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .errorDecoder(new MyErrorDecoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}

所有导致不在2xx范围内的HTTP状态的响应都将触发ErrorDecoder的decode方法,允许您处理响应、将故障包装成自定义异常或执行任何附加处理。如果希望再次重试请求,则抛出RetryableException。这将调用已注册的Retryer。


Request Interceptors

无论target是什么,都需要更改所有请求时,可以考虑配置一个RequestInterceptor。例如,如果您充当中介,您可能希望传播x - forwarding -For头。

static class ForwardedForInterceptor implements RequestInterceptor {
  @Override public void apply(RequestTemplate template) {
    template.header("X-Forwarded-For", "origin.host.com");
  }
}

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new ForwardedForInterceptor())
                 .target(Bank.class, "https://api.examplebank.com");
  }
}

另一个常见的拦截器示例是身份验证,例如使用内置的 BasicAuthRequestInterceptor.

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new BasicAuthRequestInterceptor(username, password))
                 .target(Bank.class, "https://api.examplebank.com");
  }
}

Retry

默认情况下,无论HTTP方法如何,Feign都会自动重试IOExceptions,将它们视为暂时的网络相关异常,以及从ErrorDecoder抛出的任何RetryableException。要自定义此行为,请通过生成器注册一个自定义Retryer实例。

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .retryer(new MyRetryer())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}

Retryers负责通过从continueOrPropagate(RetryableException e);方法返回true或false
将为每个客户端执行创建一个Retryer实例,允许您在需要时维护每个请求的状态。
(RetryableException e)来确定重试是否发生;将为每个客户端执行创建一个Retryer实例,允许您在需要时维护每个请求的状态。
如果确定重试不成功,则会抛出最后一个RetryException。要抛出导致失败重试的原始原因,请使用exceptionPropagationPolicy()选项构建您的伪客户端。

参考文章:

发布了2 篇原创文章 · 获赞 0 · 访问量 32

猜你喜欢

转载自blog.csdn.net/C_Wangjq/article/details/105350764