9

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

Newbie
  • 7,031
  • 9
  • 60
  • 85
  • 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. – spencergibb Mar 15 '16 at 21:55
  • @spencergibb In other words, for a given client, I want to override any existing interceptors using a custom client config. This is surprisingly difficult. – Newbie Mar 16 '16 at 03:24
  • 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. :-( – Newbie Mar 16 '16 at 03:38
  • 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? – spencergibb Mar 16 '16 at 03:40
  • That's a good workaround – spencergibb Mar 16 '16 at 03:42
  • Yes, the parent context interceptors leak into each custom client configuration (when beanNamesForTypeIncludingAncestors gets called). – Newbie Mar 16 '16 at 04:03
  • I wouldn't call it leaking. It's defaults. All the other objects replace, except the interceptors which add. – spencergibb Mar 16 '16 at 04:11
  • 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. – Newbie Mar 16 '16 at 04:13

4 Answers4

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));
    };
}
}
fungbo
  • 31
  • 2
1

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 { ... }
luckystones
  • 301
  • 4
  • 14
  • i am trying this way, but that still unrelated (by config as yours) request interceptor is still being hit – greengold Oct 05 '22 at 09:55
  • Did you set up a new Configuration class for every client? @greengold – luckystones Oct 06 '22 at 07:03
  • 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 { ... } – luckystones Oct 06 '22 at 07:06
  • 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. – greengold Oct 06 '22 at 12:53
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);
Bahadir Tasdemir
  • 10,325
  • 4
  • 49
  • 61
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.

Yashasvi.G
  • 43
  • 7