编写多分支 Mono flatMap 的任何更好的方法
Any better way to write a multiple branched Mono flatMap
非响应式样式代码如下:
public Response handleRequest(LoginContext ctx) {
String username = ctx.getParameterMap().get("username");
String password = ctx.getParameterMap().get("password");
String ipAddr = ctx.getIpAddr();
if (isIpAddressBlocked(ipAddr)) {
return new Response("Your ip is blocked");
}
List<User> list = userCrudRepository.findByMobile(username);
if (CollectionUtils.isEmpty(list)) {
return new Response("User not found");
}
if (list.size() > 1) {
return new Response("Data abnormal, please contact the site administrator");
}
User user = list.get(0);
if (DigestUtils.md5Hex(password).equals(user.getPassword())) {
doWhenLoginSuccessfully(user);
return new Response("Login successfully");
} else {
return new Response("Username or password error");
}
}
为了将其重写为响应式样式,我将其更改为如下所示:
@Autowired
private UserCrudRepository userCrudRepository;
private Map<String, Boolean> blockedIpMap = new ConcurrentHashMap<>();
private Mono<Boolean> isIpAddressBlocked(String ipAddr) {
return Mono.just(blockedIpMap.containsKey(ipAddr));
}
private Mono<Void> doWhenLoginSuccessfully(LoginContext ctx, User user) {
ctx.getSession().getAttributes().put("loginStatus", 1);
ctx.getSession().getAttributes().put("userId", user.getId());
// This method is not finished yet.
return Mono.just(0).then();
}
public Mono<Response> handleRequest(LoginContext ctx) {
String username = ctx.getParameterMap().get("username");
String password = ctx.getParameterMap().get("password");
String ipAddr = ctx.getIpAddr();
return isIpAddressBlocked(ipAddr)
.filter((x)->x==Boolean.FALSE)
.flatMapMany((x)->userCrudRepository.findByMobile(username))
.collectList()
.flatMap((x)->{
if (CollectionUtils.isEmpty(x)) {
return Mono.just(new Response("User not found"));
} else if (x.size() > 1) {
return Mono.just(new Response("Data abnormal, please contact the site administrator"));
} else {
User user = x.get(0);
if (DigestUtils.md5Hex(password).equals(user.getPassword())) {
return doWhenLoginSuccessfully(ctx, user)
.thenReturn(new Response("Login successfully"));
} else {
return Mono.just(new Response("Username or password error"));
}
}
})
.switchIfEmpty(Mono.just(new Response("Your ip is blocked")));
}
但是flatMap中的ifs一点都不令人满意
我想知道是否有更好的方法来做到这一点。
感谢您的建议。
这是一个基于意见的问题,但我会如何做。
package com.vob.reactive.webflux.service;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import static java.util.Arrays.asList;
@Component
@Slf4j
public class CustomService {
private List<String> blockedIpMap = asList("127.0.0.1");
private List<User> users = asList(
new User("invalid", DigestUtils.md5Hex("password")),
new User("invalid", DigestUtils.md5Hex("password1")),
new User("valid", DigestUtils.md5Hex("password"))
);
private Mono<Boolean> isIpAddressBlocked(String ipAddr) {
return Mono.justOrEmpty(blockedIpMap.stream().filter(ip -> ip.equals(ipAddr)).findFirst())
.flatMap(c -> Mono.just(true))
.defaultIfEmpty(false);
}
private Mono<Boolean> doWhenLoginSuccessfully(User user) {
// This method is not finished yet.
return Mono.just(true);
}
public Mono<User> findByMobile(String username, String password) {
return Flux.fromStream(users.stream().filter(u -> u.username.equals(username)))
.collectList()
.flatMap(x -> {
if (CollectionUtils.isEmpty(x)) {
return Mono.error(new IllegalArgumentException("User not found"));
} else if (x.size() > 1) {
return Mono.error(new IllegalArgumentException("Data abnormal, please contact the site administrator"));
}
User user = x.get(0);
if (!DigestUtils.md5Hex(password).equals(user.getPassword())) {
return Mono.error(new IllegalArgumentException("Username or password error"));
}
return Mono.just(user);
});
}
public Mono<String> handleRequest(String username, String password, String ipAddr) {
return isIpAddressBlocked(ipAddr)
.flatMap(isBlocked->{
if(isBlocked){
return Mono.error(new IllegalArgumentException("Your ip is blocked"));
}
return Mono.just(false);
})
.flatMap((x) -> findByMobile(username, password))
.flatMap(this::doWhenLoginSuccessfully)
.flatMap(x-> Mono.just("Login successfully"));
}
@Data
public class User {
private final String username;
private final String password;
}
}
然后测试
package com.vob.reactive.webflux.service;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
class CustomServiceTest {
@Test
public void success() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password", "10.0.0.1"))
.expectNext("Login successfully")
.verifyComplete();
}
@Test
public void invalidPassword() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password2", "10.0.0.1"))
.expectErrorMessage("Username or password error")
.verify();
}
@Test
public void abnormal() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("invalid", "password", "10.0.0.1"))
.expectErrorMessage("Data abnormal, please contact the site administrator")
.verify();
}
@Test
public void blockedIpWithRightPassword() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password", "127.0.0.1"))
.expectErrorMessage("Your ip is blocked")
.verify();
}
}
我喜欢这种方法,因为它将成功和错误分开,然后在控制器中你可以调用服务并将其映射到 200 OK 或错误请求 400
public Mono<Object> handleExample(String username, String password, String ipAddr){
return handleRequest(username, password, ipAddr)
.onErrorMap(err-> // Return Error response)
.flatMap();
}
是的,最后你仍然有这个带有 ifs 的 flatMap,但不幸的是,这就是你的逻辑。我不太了解您的逻辑,但我宁愿使用用户和密码从数据库中执行 select,所以最后您只能获得单声道。
非响应式样式代码如下:
public Response handleRequest(LoginContext ctx) {
String username = ctx.getParameterMap().get("username");
String password = ctx.getParameterMap().get("password");
String ipAddr = ctx.getIpAddr();
if (isIpAddressBlocked(ipAddr)) {
return new Response("Your ip is blocked");
}
List<User> list = userCrudRepository.findByMobile(username);
if (CollectionUtils.isEmpty(list)) {
return new Response("User not found");
}
if (list.size() > 1) {
return new Response("Data abnormal, please contact the site administrator");
}
User user = list.get(0);
if (DigestUtils.md5Hex(password).equals(user.getPassword())) {
doWhenLoginSuccessfully(user);
return new Response("Login successfully");
} else {
return new Response("Username or password error");
}
}
为了将其重写为响应式样式,我将其更改为如下所示:
@Autowired
private UserCrudRepository userCrudRepository;
private Map<String, Boolean> blockedIpMap = new ConcurrentHashMap<>();
private Mono<Boolean> isIpAddressBlocked(String ipAddr) {
return Mono.just(blockedIpMap.containsKey(ipAddr));
}
private Mono<Void> doWhenLoginSuccessfully(LoginContext ctx, User user) {
ctx.getSession().getAttributes().put("loginStatus", 1);
ctx.getSession().getAttributes().put("userId", user.getId());
// This method is not finished yet.
return Mono.just(0).then();
}
public Mono<Response> handleRequest(LoginContext ctx) {
String username = ctx.getParameterMap().get("username");
String password = ctx.getParameterMap().get("password");
String ipAddr = ctx.getIpAddr();
return isIpAddressBlocked(ipAddr)
.filter((x)->x==Boolean.FALSE)
.flatMapMany((x)->userCrudRepository.findByMobile(username))
.collectList()
.flatMap((x)->{
if (CollectionUtils.isEmpty(x)) {
return Mono.just(new Response("User not found"));
} else if (x.size() > 1) {
return Mono.just(new Response("Data abnormal, please contact the site administrator"));
} else {
User user = x.get(0);
if (DigestUtils.md5Hex(password).equals(user.getPassword())) {
return doWhenLoginSuccessfully(ctx, user)
.thenReturn(new Response("Login successfully"));
} else {
return Mono.just(new Response("Username or password error"));
}
}
})
.switchIfEmpty(Mono.just(new Response("Your ip is blocked")));
}
但是flatMap中的ifs一点都不令人满意
我想知道是否有更好的方法来做到这一点。
感谢您的建议。
这是一个基于意见的问题,但我会如何做。
package com.vob.reactive.webflux.service;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import static java.util.Arrays.asList;
@Component
@Slf4j
public class CustomService {
private List<String> blockedIpMap = asList("127.0.0.1");
private List<User> users = asList(
new User("invalid", DigestUtils.md5Hex("password")),
new User("invalid", DigestUtils.md5Hex("password1")),
new User("valid", DigestUtils.md5Hex("password"))
);
private Mono<Boolean> isIpAddressBlocked(String ipAddr) {
return Mono.justOrEmpty(blockedIpMap.stream().filter(ip -> ip.equals(ipAddr)).findFirst())
.flatMap(c -> Mono.just(true))
.defaultIfEmpty(false);
}
private Mono<Boolean> doWhenLoginSuccessfully(User user) {
// This method is not finished yet.
return Mono.just(true);
}
public Mono<User> findByMobile(String username, String password) {
return Flux.fromStream(users.stream().filter(u -> u.username.equals(username)))
.collectList()
.flatMap(x -> {
if (CollectionUtils.isEmpty(x)) {
return Mono.error(new IllegalArgumentException("User not found"));
} else if (x.size() > 1) {
return Mono.error(new IllegalArgumentException("Data abnormal, please contact the site administrator"));
}
User user = x.get(0);
if (!DigestUtils.md5Hex(password).equals(user.getPassword())) {
return Mono.error(new IllegalArgumentException("Username or password error"));
}
return Mono.just(user);
});
}
public Mono<String> handleRequest(String username, String password, String ipAddr) {
return isIpAddressBlocked(ipAddr)
.flatMap(isBlocked->{
if(isBlocked){
return Mono.error(new IllegalArgumentException("Your ip is blocked"));
}
return Mono.just(false);
})
.flatMap((x) -> findByMobile(username, password))
.flatMap(this::doWhenLoginSuccessfully)
.flatMap(x-> Mono.just("Login successfully"));
}
@Data
public class User {
private final String username;
private final String password;
}
}
然后测试
package com.vob.reactive.webflux.service;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
class CustomServiceTest {
@Test
public void success() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password", "10.0.0.1"))
.expectNext("Login successfully")
.verifyComplete();
}
@Test
public void invalidPassword() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password2", "10.0.0.1"))
.expectErrorMessage("Username or password error")
.verify();
}
@Test
public void abnormal() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("invalid", "password", "10.0.0.1"))
.expectErrorMessage("Data abnormal, please contact the site administrator")
.verify();
}
@Test
public void blockedIpWithRightPassword() {
var service = new CustomService();
StepVerifier.create(service.handleRequest("valid", "password", "127.0.0.1"))
.expectErrorMessage("Your ip is blocked")
.verify();
}
}
我喜欢这种方法,因为它将成功和错误分开,然后在控制器中你可以调用服务并将其映射到 200 OK 或错误请求 400
public Mono<Object> handleExample(String username, String password, String ipAddr){
return handleRequest(username, password, ipAddr)
.onErrorMap(err-> // Return Error response)
.flatMap();
}
是的,最后你仍然有这个带有 ifs 的 flatMap,但不幸的是,这就是你的逻辑。我不太了解您的逻辑,但我宁愿使用用户和密码从数据库中执行 select,所以最后您只能获得单声道。