I use Spring Gateway 2021.0.8. I want to make this Post request using RestTemplate:
import org.springframework.web.client.RestTemplate;
public ResponseEntity<Response> getRemotePayload() {
HttpHeaders headers = new HttpHeaders();
headers.add("AUTHORIZATION", "test");
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("type", "test");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
return getRestTemplate()
.exchange("http://127.0.0.1:8080/users", HttpMethod.POST, request, Response.class);
}
I get error during execution:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.29.jar:3.4.29]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.web.filter.reactive.ServerWebExchangeContextFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
Do you know how I can solve this issue?
I tried this:
@Configuration
public class WebClientConfig {
@Bean
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
@Autowired
public WebClient webClient;
public Mono<Response> getResponse(......) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HttpHeaders.AUTHORIZATION, authHeader);
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
requestBody.add(.....);
Mono<Response> responseMono = webClient.method(HttpMethod.POST)
.uri("some usrl")
.headers(httpHeaders -> httpHeaders.addAll(headers))
.body(BodyInserters.fromValue(requestBody))
.retrieve()
.bodyToMono(Response.class);
Mono<Response> customObjMono = responseMono.map(customObject -> {
Response customObj = new Response();
customObj.setAccessToken(customObject.getAccessToken());
return customObj;
});
return customObjMono;
}
Mono<Response> tokden = client.getResponse(........);
AtomicReference<Response> plainJavaObject = new AtomicReference<>();
tokden.subscribe(transformedToken -> {
plainJavaObject.set(transformedToken);
});
Response token = plainJavaObject.get();
The code works but I need implementation with Feign to use eureka client resolution. I tried this:
@FeignClient(name = "http://remote/...")
public interface ClientFeign {
@PostMapping(value = "/....", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
Mono<Response> getJwt(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHeader, .....);
}
@Component
public class YourService {
private ClientFeign clientFeign;
@Autowired
public YourService(ClientFeign clientFeign) {
this.clientFeign = clientFeign;
}
public Mono<Response> getJwt(........) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
requestBody.add(.......);
return clientFeign.getJwt(.......)
.map(customObject -> {
Response customObj = new Response();
customObj.setAccessToken(customObject.getAccessToken());
return customObj;
});
}
}
AtomicReference<Response> plainJavaObject = new AtomicReference<>();
Mono<Response> tokenMono = yourService.getJwt(.........);
tokenMono.subscribe(transformedToken -> {
plainJavaObject.set(transformedToken);
});
Response token = plainJavaObject.get();
I get error for this line return clientFeign.getJwt(.......)
:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.29.jar:3.4.29]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.web.filter.reactive.ServerWebExchangeContextFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
Do you know how I can fix this issue?
EDIT: code to reproduce the issue: https://github.com/rcbandit111/gateway_reactive_feign_poc/tree/master/gateway
well , below is general usage of webclient
configure your webclient host and port , if you are using reactor-netty ,wired
@Configuration
@Slf4j
public class ClientApiBootstrap {
@Bean("server")
public HttpClient httpClient(){
return HttpClient.create()
.host("localhost") // your server host
.port(8080); // your server port
}
@Bean("webClient")
@ConditionalOnBean(HttpClient.class)
public WebClient webclient(@Autowired HttpClient client){
return WebClient
.builder()
.clientConnector(new ReactorClientHttpConnector(client))
.build();
}
}
@Component
@Slf4j
public class WebClinetApi {
@Autowired
public WebClient webClient;
public Mono<YourResponse> auth () {
return webClient
.post()
.uri("/auth")
.contentType(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatusCode::isError,
(clientResponse ) ->
clientResponse
.bodyToMono(ProblemDetail.class)
.flatMap(problemDetail ->
Mono.error(()->
new RuntimeException(problemDetail.getDetail()))))
.bodyToMono(YourResponse.class)
.doOnError((throwable)-> {
log.error( throwable.getMessage());
});
}
}
Q : Looks like latest Spring Gateway uses reactive implementation so I need a reactive client also to make a external call?
A: Actually , it is unnecessary that use reactive-programming client , it has extra api reference such as Mono, Flux , you d better that familiar with reactive-programing. and t is more complex with blocking server which based on tomcat . if you would not use it just modify your application code like following
public static void main(String[] args) {
SpringApplication application = new SpringApplication(APP.class);
application.setWebApplicationType(WebApplicationType.SERVLET);
application.run(args);
}