How to exclude RequestInterceptor for an specific Spring Cloud Feign client?
Asked Answered
M

4

10

I have a number of clients for which a "global" RequestInterceptor has been defined. For one of the clients I need this "global" interceptor to be excluded. Is it possible to override the full set of RequestInterceptors for a particular FeignClient?

@FeignClient(value = "foo", configuration = FooClientConfig.class)
public interface FooClient {
//operations
}

@Configuration
public class FooClientConfig{

//How do I exclude global interceptors from this client configuration?
}

The spring-cloud-netflix version in use is 1.1.0 M5

Musical answered 15/3, 2016 at 18:8 Comment(8)
That's an interesting question. My first guess is that you might have to extend a Feign.Builder that ignores any call to requestInterceptors or ignores the ones you want.Concision
@Concision In other words, for a given client, I want to override any existing interceptors using a custom client config. This is surprisingly difficult.Musical
It will difficult for me to maintain the list of interceptors that I don't want included in this client. Therefore, I'm not going to register global interceptors at all. Instead, every single client is going to be declared with an specific configuration attached to it. In my case, this means that I will have 2 custom feign client configurations, one for most clients and another for exceptional/one-off client. :-(Musical
It is because you can have multiple interceptors and the feign application contexts inherit from the parent. Maybe an option to NOT inherit from the parent on @FeignClient?Concision
That's a good workaroundConcision
Yes, the parent context interceptors leak into each custom client configuration (when beanNamesForTypeIncludingAncestors gets called).Musical
I wouldn't call it leaking. It's defaults. All the other objects replace, except the interceptors which add.Concision
Maybe a middle ground could be to support the two items below: 1) Having an option to not inherit from parent for feign beans(Decoder, Encoder, Contract, etc) 2) Continue to allow bean injection from parent context in the custom config class itself so that folks can re-use parent feign beans if they choose at the config class level(e.g. half the beans re-used from parent ctx and half the beans being custom without side effects). I'm not sure how possible it is to implement #2 but it would allow cherry picking what you want to use from the parent.Musical
D
3

It seems there is no easy way to override the global interceptor. I think you could do it like this:

@Configuration
public class FooClientConfig{

@Bean
RequestInterceptor globalRequestInterceptor() {
    return template -> {
        if (template.url().equals("/your_specific_url")) {
            //don't add global header for the specific url
            return;
        }

        //add header for the rest of requests
        template.header(AUTHORIZATION, String.format("Bearer %s", token));
    };
}
}
Dap answered 4/5, 2018 at 5:22 Comment(0)
I
3

Based on the issue stated here. Instead of excluding interceptors, you need to define different feign clients for each API. Add your interceptors based on your needs.

public class ConfigOne {
  @Bean
  public InterceptorOne interceptorOne(AdditionalDependency ad) {
    return new InterceptorOne(ad);
  }
}

Just make sure you don't use @Configuration annotation on above class. Instead, importing this bean on client definition would be a working solution.

@FeignClient(name = "clientOne", configuration = ConfigOne.class)
public interface ClientOne { ... }
Illusionist answered 1/8, 2022 at 9:29 Comment(4)
i am trying this way, but that still unrelated (by config as yours) request interceptor is still being hitLakeishalakeland
Did you set up a new Configuration class for every client? @LakeishalakelandIllusionist
And also make sure to put all your Interceptors in their own Config class so that you clearly isolate each client config from the other. @FeignClient(name = "clientOne", configuration = ConfigOne.class) public interface ClientOne { ... } @FeignClient(name = "clientTwo", configuration = ConfigTwo.class) public interface ClientTwo { ... }Illusionist
yeah, i figured out that key to solving this is that configuration can't be a spring @Component . then, you can declare interceptor in it which will be specific for the client's configuration.Lakeishalakeland
E
0

An enhanced way of solving this is to pass a custom header to your request like:

@PostMapping("post-path")
ResponseEntity<Void> postRequest(@RequestHeader(HEADER_CLIENT_NAME) String feignClientName, @RequestBody RequestBody requestBody);

I want to set the header in interceptor for only this feign client. Before setting the header, first, the interceptor checks HEADER_CLIENT_NAME header if exists and have the desired value:

private boolean criteriaMatches(RequestTemplate requestTemplate) {
    Map<String, Collection<String>> headers = requestTemplate.headers();
    return headers.containsKey(HEADER_CLIENT_NAME)
        && headers.get(HEADER_CLIENT_NAME).contains("feign-client-name");
}

Thus, you can check before setting the basic authentication. In interceptor:

@Override
public void apply(RequestTemplate template) {
    if (criteriaMatches(template)) {
        /*apply auth header*/
    }
}

In this way, other feign client's requests won't be manipulated by this interceptor.

Finally, I set the feignClientName to the request:

feignClient.postRequest("feign-client-name", postBody);
Electrotherapy answered 3/3, 2020 at 14:46 Comment(0)
F
0

One way to do this to remove the @Configuration annotation from the FooClientConfig class as in the current situation it is applied globally.

And then use

@FeignClient(value = "foo", configuration = FooClientConfig.class)

on all of the feign clients you want to use the config with.

Filagree answered 21/3, 2022 at 12:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.