英文:
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("question")
MyResponse question(@RequestBody MyRequest myRequest) {
List<String> myStrings = myRequest.getListOfString();
List<Integer> myIds = iterateAndSendRequestOneByOneGetIdFromString(myStrings);
return new MyResponse(myIds);
}
private List<Integer> iterateAndSendRequestOneByOneGetIdFromString(List<String> myStrings) {
List<Integer> ids = new ArrayList<>();
for (String string : myStrings) {
Integer id = new RestTemplate().postForObject("http://external-service:8080/getOneIdFromOneString", string, Integer.class);
ids.add(id);
}
return ids;
}
// @PostMapping("how-to-do-the-same-with-WebFlux-WebClient-please?")
// Mono<MyResponse> question(@RequestBody Mono<MyRequest> myRequestMono) {
// return null;
// }
}
class MyResponse {
private List<Integer> myIds;
}
class MyRequest {
private List<String> 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<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
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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论