feign拦截器--RequestInterceptor

在使用feign做服务间调用的时候,如何修改请求的头部或编码信息呢,可以通过实现RequestInterceptor接口的apply方法,feign在发送请求之前都会调用该接口的apply方法,所以我们也可以通过实现该接口来记录请求发出去的时间点。

 

以下是参考文章的内容

RequestInterceptor

feign-core-10.2.3-sources.jar!/feign/RequestInterceptor.java

 

public interface RequestInterceptor {

  /**
   * Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
   */
  void apply(RequestTemplate template);
}
  • RequestInterceptor接口定义了apply方法,其参数为RequestTemplate;它有一个抽象类为BaseRequestInterceptor,还有几个实现类分别为BasicAuthRequestInterceptor、FeignAcceptGzipEncodingInterceptor、FeignContentGzipEncodingInterceptor

BasicAuthRequestInterceptor

feign-core-10.2.3-sources.jar!/feign/auth/BasicAuthRequestInterceptor.java

 

public class BasicAuthRequestInterceptor implements RequestInterceptor {

  private final String headerValue;

  /**
   * Creates an interceptor that authenticates all requests with the specified username and password
   * encoded using ISO-8859-1.
   *
   * @param username the username to use for authentication
   * @param password the password to use for authentication
   */
  public BasicAuthRequestInterceptor(String username, String password) {
    this(username, password, ISO_8859_1);
  }

  /**
   * Creates an interceptor that authenticates all requests with the specified username and password
   * encoded using the specified charset.
   *
   * @param username the username to use for authentication
   * @param password the password to use for authentication
   * @param charset the charset to use when encoding the credentials
   */
  public BasicAuthRequestInterceptor(String username, String password, Charset charset) {
    checkNotNull(username, "username");
    checkNotNull(password, "password");
    this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset));
  }

  /*
   * This uses a Sun internal method; if we ever encounter a case where this method is not
   * available, the appropriate response would be to pull the necessary portions of Guava's
   * BaseEncoding class into Util.
   */
  private static String base64Encode(byte[] bytes) {
    return Base64.encode(bytes);
  }

  @Override
  public void apply(RequestTemplate template) {
    template.header("Authorization", headerValue);
  }
}
  • BasicAuthRequestInterceptor实现了RequestInterceptor接口,其apply方法往RequestTemplate添加名为Authorization的header

BaseRequestInterceptor

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/encoding/BaseRequestInterceptor.java

 

public abstract class BaseRequestInterceptor implements RequestInterceptor {

    /**
     * The encoding properties.
     */
    private final FeignClientEncodingProperties properties;

    /**
     * Creates new instance of {@link BaseRequestInterceptor}.
     * @param properties the encoding properties
     */
    protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
        Assert.notNull(properties, "Properties can not be null");
        this.properties = properties;
    }

    /**
     * Adds the header if it wasn't yet specified.
     * @param requestTemplate the request
     * @param name the header name
     * @param values the header values
     */
    protected void addHeader(RequestTemplate requestTemplate, String name,
            String... values) {

        if (!requestTemplate.headers().containsKey(name)) {
            requestTemplate.header(name, values);
        }
    }

    protected FeignClientEncodingProperties getProperties() {
        return this.properties;
    }

}
  • BaseRequestInterceptor定义了addHeader方法,往requestTemplate添加非重名的header

FeignAcceptGzipEncodingInterceptor

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingInterceptor.java

 

public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {

    /**
     * Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
     * @param properties the encoding properties
     */
    protected FeignAcceptGzipEncodingInterceptor(
            FeignClientEncodingProperties properties) {
        super(properties);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(RequestTemplate template) {

        addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER,
                HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
    }

}
  • FeignAcceptGzipEncodingInterceptor继承了BaseRequestInterceptor,它的apply方法往RequestTemplate添加了名为Accept-Encoding,值为gzip,deflate的header

FeignContentGzipEncodingInterceptor

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java

 

public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor {

    /**
     * Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
     * @param properties the encoding properties
     */
    protected FeignContentGzipEncodingInterceptor(
            FeignClientEncodingProperties properties) {
        super(properties);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(RequestTemplate template) {

        if (requiresCompression(template)) {
            addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER,
                    HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
        }
    }

    /**
     * Returns whether the request requires GZIP compression.
     * @param template the request template
     * @return true if request requires compression, false otherwise
     */
    private boolean requiresCompression(RequestTemplate template) {

        final Map<String, Collection<String>> headers = template.headers();
        return matchesMimeType(headers.get(HttpEncoding.CONTENT_TYPE))
                && contentLengthExceedThreshold(headers.get(HttpEncoding.CONTENT_LENGTH));
    }

    /**
     * Returns whether the request content length exceed configured minimum size.
     * @param contentLength the content length header value
     * @return true if length is grater than minimum size, false otherwise
     */
    private boolean contentLengthExceedThreshold(Collection<String> contentLength) {

        try {
            if (contentLength == null || contentLength.size() != 1) {
                return false;
            }

            final String strLen = contentLength.iterator().next();
            final long length = Long.parseLong(strLen);
            return length > getProperties().getMinRequestSize();
        }
        catch (NumberFormatException ex) {
            return false;
        }
    }

    /**
     * Returns whether the content mime types matches the configures mime types.
     * @param contentTypes the content types
     * @return true if any specified content type matches the request content types
     */
    private boolean matchesMimeType(Collection<String> contentTypes) {
        if (contentTypes == null || contentTypes.size() == 0) {
            return false;
        }

        if (getProperties().getMimeTypes() == null
                || getProperties().getMimeTypes().length == 0) {
            // no specific mime types has been set - matching everything
            return true;
        }

        for (String mimeType : getProperties().getMimeTypes()) {
            if (contentTypes.contains(mimeType)) {
                return true;
            }
        }

        return false;
    }

}
  • FeignContentGzipEncodingInterceptor继承了BaseRequestInterceptor,其apply方法先判断是否需要compression,即mimeType是否符合要求以及content大小是否超出阈值,需要compress的话则添加名为Content-Encoding,值为gzip,deflate的header

小结

  • RequestInterceptor接口定义了apply方法,其参数为RequestTemplate;它有一个抽象类为BaseRequestInterceptor,还有几个实现类分别为BasicAuthRequestInterceptor、FeignAcceptGzipEncodingInterceptor、FeignContentGzipEncodingInterceptor
  • BasicAuthRequestInterceptor实现了RequestInterceptor接口,其apply方法往RequestTemplate添加名为Authorization的header
  • BaseRequestInterceptor定义了addHeader方法,往requestTemplate添加非重名的header;FeignAcceptGzipEncodingInterceptor继承了BaseRequestInterceptor,它的apply方法往RequestTemplate添加了名为Accept-Encoding,值为gzip,deflate的header;FeignContentGzipEncodingInterceptor继承了BaseRequestInterceptor,其apply方法先判断是否需要compression,即mimeType是否符合要求以及content大小是否超出阈值,需要compress的话则添加名为Content-Encoding,值为gzip,deflate的header


值得注意的是:该拦截器所在的线程不是controller层接收到请求的线程,因为feign是利用多个线程池来发送请求的,所以,主线程中的ThreadLocal对象在这里失效了,解决方案如下(参考资料:https://bbs.huaweicloud.com/blogs/106521):
https://stackoverflow.com/questions/34719809/unreachable-security-context-using-feign-requestinterceptor 里面提到了使用 HystrixRequestVariableDefault,通过该类的注释可以知道,这个是Hystrix 为自身执行的线程提供的一个类似于ThreadLocal 的类,但是它与ThreadLocal 的不同之处在于,该Locals 的作用范围提升到了这个User Request Scope 级别,通过其注解可知,它是通过父类线程与子类线程共享的方式来共用该Locals 中的信息;所以,达到了User Request 线程与Hystrix 线程共用 attributes 的目的;由此,可以猜测,Hystrix 的线程是由当前Request 线程所创建的子线程;不过,使用的过程中,需要注意的是,HystrixRequestVariable 必须在每一个请求开始的时候进行初始化;也就是说,我们可以将 Request Context 中的有用信息存储到HystrixRequestVariableDefault中达到与Hystrix Context 共享信息;也就实现了Request Context 中的属性与Hystrix Context 之间共享的目的。

总的来说就是用HystrixRequestVariableDefault代替ThreadLocal来作为同一个请求不同环节传递变量,ThreadLocal作用于同一个线程,HystrixRequestVariableDefault作用于同一个请求。

关于HystrixRequestVariableDefault的更多说明,可以直接看该类源码注释。

具体做法看:https://bbs.huaweicloud.com/blogs/106521

 

 

 

 

 

 

 

 

  • 10
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要配置Feign拦截器,你可以使用两种方式。 第一种方式是创建一个拦截器类并实现RequestInterceptor接口。你可以自定义一个拦截器类,例如CustomFeignInterceptor,并在apply方法中实现你需要的功能,比如记录日志、增加参数、修改路径或鉴权。然后,在你的微服务的config配置中将该拦截器作为Bean注入,如下所示: ``` @Configuration public class FeignConfig { @Bean public RequestInterceptor requestInterceptor(){ return new CustomFeignInterceptor(); } } ``` 接着,在需要使用该功能的Client接口上的@FeignClient()内加上configuration = FeignConfig.class参数,以交给Spring管理,示例如下: ``` @FeignClient(value ="itemservice",path = "/item",configuration = FeignConfig.class) public interface ItemClient { @GetMapping("/list") public PageDTO<Item> page(@RequestParam("page") Integer page, @RequestParam("size") Integer size); @GetMapping("/{id}") public Item getById(@PathVariable Long id); } ``` 另一种配置方式是直接注入拦截器的Bean。在你的配置类中,可以使用@Bean注解创建一个CustomFeignInterceptor的Bean,如下所示: ``` @Bean public CustomFeignInterceptor customFeignInterceptor(){ return new CustomFeignInterceptor(); } ``` 记得在需要使用该功能的Client接口上的@FeignClient()内加上configuration参数,指定使用该拦截器的Bean,例如: ``` @FeignClient(value ="itemservice",path = "/item",configuration = YourConfigClass.class) public interface ItemClient { // API methods } ``` 这样,你就可以配置Feign拦截器了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SpringCloud 之OpenFeign 自定义配置和使用/自定义拦截器](https://blog.csdn.net/weixin_44137464/article/details/127325544)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Feign的权限访问(拦截器)](https://blog.csdn.net/weixin_52472963/article/details/129309826)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值