Use Feign with Spring WebFlux
This fork was created in order to provide support for Spring Boot 3.2 since there is an unresolved issue in the original project. Credit for the fix belongs to ramzes3333. I just relased a new version with his fix and published it to Maven Central.
Implementation of Feign on Spring WebClient. Brings you the best of two worlds together : concise syntax of Feign to write client side API on fast, asynchronous and non-blocking HTTP client of Spring WebClient.
feign-reactor-core : base classes and interfaces that should allow to implement alternative reactor Feign
feign-reactor-webclient : Spring WebClient based implementation of reactor Feign
feign-reactor-cloud : Spring Cloud implementation of reactor Feign (Ribbon/Hystrix)
feign-reactor-java11 : Java 11 HttpClient based implementation of reactor Feign (!! Winner of benchmarks !!)
feign-reactor-rx2 : Rx2 compatible implementation of reactor Feign (depends on feign-reactor-webclient)
feign-reactor-jetty : experimental Reactive Jetty client based implementation of reactor Feign (doesn't depend on feign-reactor-webclient). In future will allow to write pure Rx2 version.
- have greater reactivity level then Spring WebClient. By default don't collect body to list instead starts sending request body as stream.
- starts receiving reactive response before all reactive request body has been sent
- process Flux<
String
> correctly in request and response body
feign-reactor-spring-cloud-starter : Single dependency to have reactive feign client operabable in your spring cloud application. Uses webclient as default client implementation.
feign-reactor-bom : Maven BOM module which simplifies dependency management for all reactive feign client modules.
Write Feign API as usual, but every method of interface
- may accept
org.reactivestreams.Publisher
as body - must return
reactor.core.publisher.Mono
orreactor.core.publisher.Flux
.
@Headers({ "Accept: application/json" })
public interface IcecreamServiceApi {
@RequestLine("GET /icecream/flavors")
Flux<Flavor> getAvailableFlavors();
@RequestLine("GET /icecream/mixins")
Flux<Mixin> getAvailableMixins();
@RequestLine("POST /icecream/orders")
@Headers("Content-Type: application/json")
Mono<Bill> makeOrder(IceCreamOrder order);
@RequestLine("GET /icecream/orders/{orderId}")
Mono<IceCreamOrder> findOrder(@Param("orderId") int orderId);
@RequestLine("POST /icecream/bills/pay")
@Headers("Content-Type: application/json")
Mono<Void> payBill(Publisher<Bill> bill);
}
Build the client :
/* Create instance of your API */
IcecreamServiceApi client =
WebReactiveFeign //WebClient based reactive feign
//JettyReactiveFeign //Jetty http client based
//Java11ReactiveFeign //Java 11 http client based
.<IcecreamServiceApi>builder()
.target(IcecreamServiceApi.class, "http://www.icecreame.com")
/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
or cloud aware client :
IcecreamServiceApi client = CloudReactiveFeign.<IcecreamServiceApi>builder(WebReactiveFeign.builder())
.setLoadBalancerCommandFactory(s -> LoadBalancerCommand.builder()
.withLoadBalancer(AbstractLoadBalancer.class.cast(getNamedLoadBalancer(serviceName)))
.withRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))
.build())
.fallback(() -> Mono.just(new IcecreamServiceApi() {
@Override
public Mono<String> get() {
return Mono.just("fallback");
}
}))
.target(IcecreamServiceApi.class, "http://" + serviceName);
/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
Write Feign API as usual, but every method of interface
- may accept
Flowable
,Observable
,Single
orMaybe
as body - must return
Flowable
,Observable
,Single
orMaybe
.
@Headers({"Accept: application/json"})
public interface IcecreamServiceApi {
@RequestLine("GET /icecream/flavors")
Flowable<Flavor> getAvailableFlavors();
@RequestLine("GET /icecream/mixins")
Observable<Mixin> getAvailableMixins();
@RequestLine("POST /icecream/orders")
@Headers("Content-Type: application/json")
Single<Bill> makeOrder(IceCreamOrder order);
@RequestLine("GET /icecream/orders/{orderId}")
Maybe<IceCreamOrder> findOrder(@Param("orderId") int orderId);
@RequestLine("POST /icecream/bills/pay")
@Headers("Content-Type: application/json")
Single<Long> payBill(Bill bill);
Build the client :
/* Create instance of your API */
IcecreamServiceApi client = Rx2ReactiveFeign
.builder()
.target(IcecreamServiceApi.class, "http://www.icecreame.com")
/* Execute nonblocking requests */
Flowable<Flavor> flavors = icecreamApi.getAvailableFlavors();
Observable<Mixin> mixins = icecreamApi.getAvailableMixins();
There are 2 options:
ReactiveFeignBuilder
.addRequestInterceptor(ReactiveHttpRequestInterceptors.addHeader("Cache-Control", "no-cache"))
.addRequestInterceptor(request -> Mono
.subscriberContext()
.map(ctx -> ctx
.<String>getOrEmpty("authToken")
.map(authToken -> {
MultiValueMapUtils.addOrdered(request.headers(), "Authorization", authToken);
return request;
})
.orElse(request)));
You can use @RequestHeader
annotation for specific parameter to pass one header or map of headers
@RequestHeader example
You can enable auto-configuration of reactive Feign clients as Spring beans just by adding feign-reactor-spring-configuration
module to classpath.
Spring Auto-Configuration module
Sample cloud auto-configuration project with Eureka/WebFlux/ReaciveFeign
Library distributed under Apache License Version 2.0.