Spring WebFlux – 使用WebClient为Mono中的每个元素发送HTTP请求

huangapple go评论65阅读模式
英文:

Spring WebFlux - Send HTTP requests with WebClient for each element of a list inside a Mono

问题

我们正在尝试将一个MVC、resttemplate、阻塞式应用转换成一个WebFlux应用。

在“阻塞世界”中非常简单,从请求有效负载中获取一个列表,遍历列表并向第三方REST API发送N个HTTP请求。

非常重要的是,这是一个第三方的REST API,无法对其进行任何控制,也不能要求他们实现接受列表的版本,必须一个接一个地处理。

使用resttemplate很简单,WebFlux的等效方法是什么呢?
这有点具有挑战性,因为它接受一个Mono并返回一个Mono。
以下是一个简短的代码片段。

@SpringBootApplication
@RestController
public class QuestionApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuestionApplication.class, args);
    }

    @PostMapping("question")
    MyResponse question(@RequestBody MyRequest myRequest) {
        List<String> myStrings = myRequest.getStrings();
        return iterateAndSendRequestOneByOneGetIdFromString(myStrings)
            .collectList()
            .map(MyResponse::new)
            .block();
    }

    private Flux<Integer> iterateAndSendRequestOneByOneGetIdFromString(List<String> myStrings) {
        return Flux.fromIterable(myStrings)
            .flatMap(string ->
                WebClient.create()
                    .post()
                    .uri("http://external-service:8080/getOneIdFromOneString")
                    .bodyValue(string)
                    .retrieve()
                    .bodyToMono(Integer.class)
            );
    }
}

class MyResponse {
    private List<Integer> myIds;

    public MyResponse(List<Integer> myIds) {
        this.myIds = myIds;
    }
}

class MyRequest {
    private List<String> strings;

    public List<String> getStrings() {
        return strings;
    }
}
英文:

We are trying to convert an MVC, resttemplate, blocking, application into a WebFlux app.

Very straightforward in the “blocking world”, get a list inside a request payload, iterate through it and send N http requests to a third party rest API.

Very important it is a third party rest API, no control over it at all, and cannot ask them to implement a version taking the list, it has to be one by one.

Trivial with resttemplate, what would be the WebFlux equivalent please?
This is a bit challenging, since it takes a Mono and returns a Mono.
A small snippet will be great.

Thank you

@SpringBootApplication
@RestController
public class QuestionApplication {
public static void main(String[] args) {
SpringApplication.run(QuestionApplication.class, args);
}
@PostMapping(&quot;question&quot;)
MyResponse question(@RequestBody MyRequest myRequest) {
List&lt;String&gt;  myStrings = myRequest.getListOfString();
List&lt;Integer&gt; myIds     = iterateAndSendRequestOneByOneGetIdFromString(myStrings);
return new MyResponse(myIds);
}
private List&lt;Integer&gt; iterateAndSendRequestOneByOneGetIdFromString(List&lt;String&gt; myStrings) {
List&lt;Integer&gt; ids = new ArrayList&lt;&gt;();
for (String string : myStrings) {
Integer id = new RestTemplate().postForObject(&quot;http://external-service:8080/getOneIdFromOneString&quot;, string, Integer.class);
ids.add(id);
}
return ids;
}
//    @PostMapping(&quot;how-to-do-the-same-with-WebFlux-WebClient-please?&quot;)
//    Mono&lt;MyResponse&gt; question(@RequestBody Mono&lt;MyRequest&gt; myRequestMono) {
//        return null;
//    }
}
class MyResponse {
private List&lt;Integer&gt; myIds;
}
class MyRequest {
private List&lt;String&gt; strings;
}

答案1

得分: 1

使用flatMap从Flux中获得解决方案。

public Mono<MyResponse> getIdsFromStrings(MyRequest myRequest) {

  WebClient client =
      WebClient.builder().baseUrl("http://external-service:8080").build();

  return Flux.fromIterable(myRequest.getStrings())
      .flatMap(s -> client.post().uri("/getOneIdFromOneString").body(s, String.class).retrieve().bodyToMono(Integer.class))
      .collectList()
      .map(MyResponse::new);
}

.flatMap是一个异步操作,将同时执行您的请求。您还可以选择使用flatMap的重载方法来设置并发限制(请参阅文档)。

英文:

The way to go is to use flatMap from Flux.

public Mono&lt;MyResponse&gt; getIdsFromStrings(MyRequest myRequest) {
WebClient client =
WebClient.builder().baseUrl(&quot;http://external-service:8080&quot;).build();
return Flux.fromIterable(myRequest.getStrings())
.flatMap(s -&gt; client.post().uri(&quot;/getOneIdFromOneString&quot;).body(s, String.class).retrieve().bodyToMono(Integer.class))
.collectList()
.map(MyResponse::new);
}

.flatMap is an asynchronous operation and will execute your requests concurrently. You can also choose to set concurrency limits by using an overloaded method of flatMap (refer to documentation).

huangapple
  • 本文由 发表于 2020年8月25日 05:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/63568709.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定