前言
这是一篇关于SpringCloud中使用webclient调用微服务的总结性文章,使用期间遇到好多问题及其解决方法,希望帮助到大家。
准备
首先我们在SpringCloud项目中配置WebClient,如下所示:
@Component
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
然后在方法中引用:
@Resource
private WebClient.Builder webClientBuilder;
下面来说明一下如何调用?
操作
主要围绕get和post请求来请求微服务接口数据,如下:
1、Get请求
public Mono<FileShare> getSharedFriends(String fileId, LoginUser loginUser) {
try {
ObjectMapper mapper = new ObjectMapper();
String userJson = mapper.writeValueAsString(loginUser);
WebClient webClient = webClientBuilder.baseUrl("http://space-service").build();
return webClient.get()
.uri(uriBuilder -> uriBuilder
// 注意:path中不能添加http://space-service,应该在上面添加
.path("/crud/file/getSharedFriends")
.queryParam("fileId", fileId)
.build())
// 将用户信息传递给下游接口
.header(UserContext.USER, userJson)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<ResultSuccess<Object>>() {})
.flatMap(resultSuccess -> {
log.info("resultSuccess={}", JSONArray.toJSONString(resultSuccess));
if (resultSuccess == null || resultSuccess.getData() == null) {
log.error("Received null data from server");
return Mono.empty(); // 避免 NullPointerException
}
log.info("resultSuccess.getData()={}", resultSuccess.getData());
ObjectMapper objectMapper = new ObjectMapper(); // Jackson ObjectMapper
FileShare fileShare = objectMapper.convertValue(resultSuccess.getData(), FileShare.class);
return Mono.just(fileShare);
})
.onErrorResume(e -> {
log.error("Error retrieving FileShare: {}", e.getMessage());
return Mono.empty();
});
} catch (Exception ex) {
log.info("getSharedFriends Exception ={}", ex.getMessage());
return Mono.empty();
}
}
解释一下上面的代码:
- 我们在header中添加请求头包含用户信息数据传递给下游接口
- webClientBuilder.baseUrl中设置服务地址
- 使用bodyToMono转换返回来的数据,当然你可以写自己的类型或者
String.class
- flatMap处理转换好的数据并返回
- 如果出现异常使用onErrorResume来处理
- queryParam添加?请求参数
然后我们可以处理接口返回来的数据
Mono<FileShare> fs = spaceWebClient.getSharedFriends(fileId, loginUser);
return fs.switchIfEmpty(Mono.defer(() -> {
// 返回一个空的 FileShare 对象,以保持类型一致
FileShare emptyFileShare = new FileShare(); // 或者根据你的需求设置合适的默认值
return Mono.just(emptyFileShare); // 返回类型为 Mono<FileShare>
})
).flatMap(fileShare -> {
log.info("fileShare = {}", fileShare);
List<String> uids = new ArrayList<>();
List<User> user;
uids.add(loginUser.getUid());
if (fileShare == null || fileShare.getFriendIds() == null || fileShare.getFriendIds().isEmpty()) {
user = userService.getUserByName(uids, userName, 5);
return Mono.just(ResponseEntity.ok(new ResultSuccess<>(user)));
} else {
uids.addAll(fileShare.getFriendIds());
user = userService.getUserByName(uids, userName, 5);
}
return Mono.just(ResponseEntity.ok(new ResultSuccess<>(user)));
});
注意:如果webclient方法中返回Mono.empty(),则不会进入flatMap方法中,所以我们在switchIfEmpty方法中默认设置一个值
上面的flatMap处理你返回的接口数据,这样就完成了Get请求示例,下面看看Post请求。
2、Post请求
跟Get一样,代码如下:
public Mono<List<NotifyRemind>> queryNotifyBy(LoginUser loginUser, String senderId, String objId, List<String> recipientIds) {
try {
ObjectMapper mapper = new ObjectMapper();
NotifyRemindRequest notifyRemindRequest = new NotifyRemindRequest();
notifyRemindRequest.setSenderId(senderId);
notifyRemindRequest.setObjectId(objId);
notifyRemindRequest.setRecipientIds(recipientIds);
String userJson = mapper.writeValueAsString(loginUser);
WebClient webClient = webClientBuilder.baseUrl("http://notify-service").build();
return webClient.post()
.uri(uriBuilder -> uriBuilder
// 注意:path中不能添加http://space-service,应该在上面添加
.path("/crud/remind/queryBy")
.build())
.bodyValue(notifyRemindRequest)
// 将用户信息传递给下游接口
.header(UserContext.USER, userJson)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<ResultInfo<Object>>() {})
.flatMap(resultInfo -> {
log.info("resultSuccess={}", JSONArray.toJSONString(resultInfo));
if (resultInfo == null || resultInfo.getData() == null) {
List<NotifyRemind> empty = new ArrayList<>();
log.error("Received null data from server");
return Mono.just(empty); // 避免 NullPointerException
}
ObjectMapper objectMapper = new ObjectMapper(); // Jackson ObjectMapper
// 使用 TypeReference 来指定目标类型
List<NotifyRemind> notifyReminds = objectMapper.convertValue(
//注意:不要使用List.class,因为返回的是List<LinkedHashMap>,改成:new TypeReference<>() {}
resultInfo.getData(), new TypeReference<>() {});
return Mono.just(notifyReminds);
})
.onErrorResume(e -> {
log.error("Error retrieving FileShare: {}", e.getMessage());
return Mono.empty();
});
} catch (Exception ex) {
log.info("getSharedFriends Exception ={}", ex.getMessage());
return Mono.empty();
}
}
除了bodyValue添加请求参数类,其它的跟Get请求类似,不过有个注意点:
objectMapper.convertValue转换成自己想要的List<NotifyRemind>类型时
请使用:
objectMapper.convertValue(resultInfo.getData(), new TypeReference<>() {})
不要使用:
objectMapper.convertValue(resultInfo.getData(), List.class)
接着我们在Controller层来接收下返回的接口数据:
Mono<List<NotifyRemind>> listMono = notifyWebClient.queryNotifyBy(loginUser, loginUser.getUid(), fileId, fids);
listMono.subscribe(res -> {
log.info("result:{}", res);
if (res.isEmpty()) {
for (String fid : fids) {
sendNotify(loginUser, file, fid);
}
} else {
// 找出 fids 中不存在于 notifyRemind.id 的值
List<String> missingIds = fids.stream()
.filter(fid -> res.stream().noneMatch(recipient -> recipient.getRecipientId().equals(fid)))
.collect(Collectors.toList());
for (String fid : missingIds) {
sendNotify(loginUser, file, fid);
}
}
});
总结
1、Mono如果你没使用的话则它不会请求接口,如:
Mono<FileShare> fs = spaceWebClient.getSharedFriends(fileId, loginUser);
这段代码它不会请求接口,只有加上上面的.flatMap才会正常请求
2、不要把WebClient的请求放到循环中,如while和for
3、path是设置路径,服务名需要在webClientBuilder.baseUrl
中设置
4、因为我不想返回数据,所以使用.subscribe
方法来接收
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。