Is your feature request related to a problem? Please describe. Our spring-cloud-openfeign clients have a default configuration in a starter. Only one showed for example.

feign.client.config.default.default-request-headers.accept=application/json

In some rare cases we implement specific clients that need different values. (for example purpose)

feign.client.config.specific-client.default-request-headers.accept=application/xml

Unfortunately FeignClientFactoryBean.addDefaultRequestHeaders() (version 3.1.2) lines 326 to 328 contain:

if (!headers.containsKey(key)) {
    requestTemplate.header(key, defaultRequestHeaders.get(key));
}

specific-client config always passes after default client config, therefore the accept header value is never application/xml.

Describe the solution you'd like It would be nice (and backward compatible) if there was a config property to allow for default config to be applied after specific ones if enabled. Something like:

feign.client.config.apply-default-after=true

with the default value being false.

Describe alternatives you've considered We are still trying to figure out a way to override.

Additional context spring-cloud-openfeign version 3.1.2

Comment From: OlgaMaciaszek

Hello, @pgehl The model where client-specific config overrides default config has been adopted on purpose and is consistent throughout the Spring Cloud projects wherever applicable. We're not planning on changing it. Allowing changing the override direction would introduce inconsistency and would be quite messy and possibly cause maintenance issues.

Comment From: pgehl

Hello @OlgaMaciaszek, Thank you for the reply. Our issue is that the client specific config does not override the default config.

Comment From: fengyj

have the same issue.

Hi @OlgaMaciaszek, isn't it a bug? it doesn't make sense that the config of a particular service cannot override the default one.

Hi, @pgehl , have you resolved this issue finally? Thanks.

Comment From: fengyj

        if (properties != null && inheritParentContext) {
            if (properties.isDefaultToProperties()) {
                configureUsingConfiguration(context, builder);
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(contextId), builder);
            }
            else {
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(contextId), builder);
                configureUsingConfiguration(context, builder);
            }
        }
    private void addDefaultRequestHeaders(FeignClientProperties.FeignClientConfiguration config,
            Feign.Builder builder) {
        Map<String, Collection<String>> defaultRequestHeaders = config.getDefaultRequestHeaders();
        if (Objects.nonNull(defaultRequestHeaders)) {
            builder.requestInterceptor(requestTemplate -> {
                Map<String, Collection<String>> headers = requestTemplate.headers();
                defaultRequestHeaders.keySet().forEach(key -> {
                    if (!headers.containsKey(key)) {
                        requestTemplate.header(key, defaultRequestHeaders.get(key));
                    }
                });
            });
        }
    }

according to this code, the default config is used to set the header first, then the config of the particular service.

Comment From: OlgaMaciaszek

@pgehl @fengyj It should override. Please provide a minimal, complete, verifiable example that reproduces the issue - preferably as a link to a repo with a small executable app or tests.

Comment From: fengyj

hi @OlgaMaciaszek, thanks for the replying. I've uploaded a sample project here, https://github.com/fengyj/feigndemo/tree/master. there are two unit tests, one is using the config without the default config, and another one with the default config and overrides the default header value in the particular client config. You will see the second one is failed with the 406 Not Acceptable error.

// without the default config
@SpringBootTest
@EnableFeignClients(basePackages = "me.fengyj.feigndemo")
class YahooFinancialServiceTest {

    @Autowired
    private YahooFinancialService svc;

    @Test
    public void test_getHistoryPrice() {

        var response = svc.getHistoryPrices("600036.SS");

        Assertions.assertNotNull(response);
        Assertions.assertTrue(response.startsWith("Date,Open,High,Low,Close,Adj Close,Volume\n"));
    }
}
// with the default config
@SpringBootTest
@EnableFeignClients(basePackages = "me.fengyj.feigndemo")
@ActiveProfiles("defaultconfig")
class YahooFinancialServiceDefaultConfigTest {

    @Autowired
    private YahooFinancialService svc;

    @Test
    public void test_getHistoryPrice() {

        var response = svc.getHistoryPrices("600036.SS");

        Assertions.assertNotNull(response);
        Assertions.assertTrue(response.startsWith("Date,Open,High,Low,Close,Adj Close,Volume\n"));
    }
}

Comment From: OlgaMaciaszek

Thanks, @pgehl. Was able to reproduce it. Will provide a fix.