首先需求,是做一个短网址的服务,之前用 Spring MVC 的时候的代码,非常直观明了
@PostMapping("/generate")
public Map<String, String> generate(@RequestBody Map<String, String> map) {
if (!"sdjsioadijosaoidsa".equals(map.get("token"))) {
throw new RuntimeException("无权限");
}
String url = map.get("url");
Assert.isTrue(!StringUtils.isEmpty(url), "url absent");
byte[] sha256 = DigestUtils.sha256(url);
String path = redisOpt.get(sha256);
if (path == null) {
path = RandomStringUtils.randomAlphanumeric(6);
redisOpt.set(path.getBytes(StandardCharsets.ISO_8859_1), url, 365, TimeUnit.DAYS);
redisOpt.set(sha256, path, 365, TimeUnit.DAYS);
}
return ImmutableMap.of("url", urlPrefix + path);
}
然后换了 WebFlux,尝试写一下比较 Reactive 风格的代码
public Mono<ServerResponse> generate(ServerRequest serverRequest) {
return serverRequest.bodyToMono(String.class).map(JSON::parseObject)
.flatMap(jsonObject -> {
if (!"sdjsioadijosaoidsa".equals(jsonObject.get("token"))) {
return ServerResponse.status(HttpStatus.UNAUTHORIZED)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(ImmutableMap.of("errorMsg", "no privilege"));
}
String url = jsonObject.getString("url");
if (StringUtils.isEmpty(url)) {
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(ImmutableMap.of("errorMsg", "url is absent"));
}
byte[] sha256 = DigestUtils.sha256(url);
return redisTemplate.opsForValue().get(sha256)
.flatMap(path -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(ImmutableMap.of("url", urlPrefix + path)))
.switchIfEmpty(Mono.just(RandomStringUtils.randomAlphanumeric(6))
.filterWhen(newPath -> redisTemplate.opsForValue()
.set(newPath.getBytes(), url, Duration.ofDays(365)))
.filterWhen(newPath -> redisTemplate.opsForValue()
.set(sha256, newPath, Duration.ofDays(365)))
.flatMap(newPath -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(ImmutableMap.of("url", urlPrefix + newPath)))
.switchIfEmpty(ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(ImmutableMap.of("errorMsg", "Internal Server Error"))));
});
}
这个代码,说实话,一周后估计我自己都看不懂了。
其实我想问一下,针对逻辑稍微比较复杂的时候,响应式编程该怎么做,尤其是对这种条件分支比较多的时候。
这个代码该怎么优化?
现在我做的分支处理不外乎两种方式
- flatMap,拿到数据,然后就是写 if-else。这种方式有些不好用的地方,就是数据在上个流没下来,压根就进不了 flatMap (比如例子中的 redis 未命中数据)
- switchIfEmpty,在 switchIfEmpty 之前,分支使用 filter|filterWhen 这些,stream.filterWhen(if 条件).xxx(if 分支).switchIfEmpty(else 分支)。在这种情况下做 if-elseif 就懵逼了 我是这方面的新人,而且没有人指导,全靠自己想和骨锅。可能是自己用错了,请大神指点迷津!