未使用 Feign 配置

Feign configuration not being used

我在 Spring 启动 API 中使用 Feign,我想使用以下配置它:

import feign.RequestInterceptor;
import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.ErrorDecoder;
import feign.okhttp.OkHttpClient;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignClientConfiguration {

    @Bean
    public ErrorDecoder errorDecoder() {
        return new ErrorDecoder.Default();
    }

    @Bean
    public OkHttpClient client() {
        return new OkHttpClient();
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("user", "some user");
            requestTemplate.header("password", "some password");
            requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
        };
    }


}

问题是,如果我在服务中使用以下代码,我会收到 401,似乎没有提供身份验证数据:

        Feign.builder()
        .client(new OkHttpClient())
        .encoder(new GsonEncoder())
        .decoder(new GsonDecoder())
        .logger(new Slf4jLogger(Npa.class))
        .target(AClass.class, "https://some.website");

如果我只添加一行指定用户和密码就可以了:

    Feign.builder()
    .client(new OkHttpClient())
    .encoder(new GsonEncoder())
    .decoder(new GsonDecoder())
    .logger(new Slf4jLogger(Npa.class))
    .requestInterceptor(new BasicAuthRequestInterceptor("some user", "some password"))
    .target(AClass.class, "https://some.website");

我更愿意使用配置,但我不明白它有什么问题。

由于您已经创建了一些 bean,因此您不需要在构建步骤中再次手动实例化它们。

pseudo-class 可以创建假客户端的地方可能是这样的:

@Component
public class FeignClientFactory {

    @Autowired
    private ErrorDecoder errorDecoder;

    @Autowired
    private OkHttpClient client;

    @Autowired
    private RequestInterceptor requestInterceptor;

    public AClass getFeignClient() {
        return Feign.builder()
                .client(client)
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .logger(new Slf4jLogger(Npa.class))
                .requestInterceptor(requestInterceptor)
                .target(AClass.class, "https://some.website");
    }
}

当然,有很多选项可以改进它。 F.e., 你可以创建其他 bean(用于编码器、记录器等)或使用默认(自动配置)的 bean。

或者如果你只需要一个假客户端 bean,你可以在 @Configuration class 中完成。

如果是请求拦截器 - 还有另一个构建器步骤 .requestInterceptors,您可以在其中传递 collection 个。

编辑: 对于基本身份验证,您不需要单独的用户名和密码 header,而是一个 header "Authorization" .它的值应该类似于 "Basic username:password",其中 "username:password" 部分应该使用 Base64 (link) 编码。

所以,在你的 @Configuration class 你可以这样写:

@Bean
public RequestInterceptor requestInterceptor() {
    return requestTemplate -> {
        String encodedCredentials = Base64.getEncoder()
            .encodeToString("some user:some password".getBytes());
        requestTemplate.header("Authorization", "Basic " + encodedCredentials);
        requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
    };
}

然后你就可以使用这个bean来构建我上面描述的Feign客户端了。

另一种选择:因为创建授权header是一个很常见的任务,所以BasicAuthRequestInterceptor是由Feign引入的,它也做同样的事情。您可以改用它。所以现在,在 @Configuration class 中你可以创建两个 beans:

    @Bean
    @Qualifier("basicAuthRequestInterceptor")
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("some user","some password");
    }

    @Bean
    @Qualifier("customRequestInterceptor")
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
        };
    }

在这种情况下,您需要修改 feign 客户端构建方法以应用这两个拦截器:

@Component
public class FeignClientFactory {

    @Autowired
    private ErrorDecoder errorDecoder;

    @Autowired
    private OkHttpClient client;

    @Autowired
    @Qualifier("basicAuthRequestInterceptor")
    private RequestInterceptor basicAuthRequestInterceptor;

    @Autowired
    @Qualifier("customRequestInterceptor")
    private RequestInterceptor requestInterceptor;

    public AClass getFeignClient() {
        List<RequestInterceptor> requestInterceptors = new ArrayList<>();
        requestInterceptors.add(basicAuthRequestInterceptor);
        requestInterceptors.add(requestInterceptor);

        return Feign.builder()
                .client(client)
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .logger(new Slf4jLogger(Npa.class))
                .requestInterceptors(requestInterceptors)
                .target(AClass.class, "https://some.website");
    }
}