0

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

Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • show the section of main application code snippet, ithe error stack demonstrate you use reactor netty as server context , guess that may be you are use netty as reactive server context . this is a reactive context , but the restTemplate is blocking http request. – Peng Jul 16 '23 at 15:31
  • if you really have the needed of reactive-programing http request , you d better read the spring doc [reactive web](https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.reactive) the introduce of webClient section – Peng Jul 16 '23 at 15:40
  • ok, I updated the post but I need help for the above code. – Peter Penzov Jul 16 '23 at 15:47
  • Looks like latest Spring Gateway uses reactive implementation so I need a reactive client also to make a external call? – Peter Penzov Jul 16 '23 at 15:57
  • Spring Cloud Gateway has always required Webflux/reactive – spencergibb Jul 16 '23 at 20:46

1 Answers1

1

well , below is general usage of webclient

Configure 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();
    }


}

Defination webclient api


@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());
                });
    }
}

Appendix

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);

    }
Peng
  • 653
  • 8
  • Thanks, I have 3 questions. What should be the proper way to set custom header and body like the original code. Should I return a plain Java Object or a `Mono`? – Peter Penzov Jul 16 '23 at 16:03
  • both of them is ok , – Peng Jul 16 '23 at 16:11
  • ok, I updated the post. Can you show me the last issue to change `Mono` to `ResponseEntity`. Is it's possible how I can keep the original code with RestTemplate? – Peter Penzov Jul 16 '23 at 16:14
  • no , There is some dfference between reactive and servlet web Container . first, as previously, ```restTemplate``` is used for blocking http request (the last ```fegin``` is supported reactive ,you can have a try, ), webclient is used for reactive request , it is two different way do a request , both usage and design idea is greatly different. By the way the [spring doc] (https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web) have more technical demonstration – Peng Jul 17 '23 at 02:12
  • Can you help me to fix the above issue with Feign, please? – Peter Penzov Jul 18 '23 at 16:05
  • @Peter Penzov show me your project , you can post it on github , i would checkout and modify project directly – Peng Jul 18 '23 at 16:15
  • Here it is https://github.com/rcbandit111/gateway_reactive_feign_poc/tree/master/gateway – Peter Penzov Jul 18 '23 at 21:32
  • I added you here https://github.com/rcbandit111/gateway_reactive_feign_poc – Peter Penzov Jul 19 '23 at 08:19
  • @Peter Penzov done , have a look on github – Peng Jul 19 '23 at 13:17
  • Is it possible to make the code much more simple like this: https://pastebin.com/BVBAiqPU and also after that how I can get a simple `BasicTokenResponseDto` Java object? – Peter Penzov Jul 19 '23 at 15:47
  • that s ok , block() to get BasicTokenResponseDto – Peng Jul 20 '23 at 07:12
  • with .block() I get the above exception. Can you push a simplified version of the code please? – Peter Penzov Jul 20 '23 at 23:01
  • @ Peter Penzov done , have a look on github. this is because of the difference between blocking and non-blocking thread model , emmm , may be it is diffcult for green hand to dstinguish both of them advantages and usages . in a nutshell, non-blocking model is more complex but resoures saving – Peng Jul 21 '23 at 13:40
  • Thanks. Can you help me to find what is wrong into the code I pushed, please? See main branch – Peter Penzov Jul 22 '23 at 16:51
  • ref: https://stackoverflow.com/questions/76740085/java-lang-nullpointerexception-the-mono-returned-by-the-supplier-is-null – Peter Penzov Jul 22 '23 at 17:01