将 NodeJS 异步代码转换为 Spring Project Reactor

Convert NodeJS asynchronous code to Spring Project Reactor

我有以下 NodeJS 代码:

// req and resp are http request, response objects
var uri = req.getURI()
var pageView = new PageView(uri)

var token = req.token
if (token) {
    UserRepository.findByToken(token, function(notFound, user){
        if(notFound) {  // means  user not found by specified token
            var newUser = new User('John Doe')
            user.foo = 'some value'
            processUser(newUser, pageView)

        } else {  // user found by token
            user.foo = 'some value'
            processUser(user, pageView)
        }
    })
} else {  // token does not exist
    token = new Token('some value')
    resp.setToken(token)
    var newUser = new User('John Doe')
    user.foo = 'some value'

    processUser(newUser, pageView)

}



 processUser(user, pageView) {

    PageViewRepositiry.save(pageView, function(error, savedPageView){
        if(error) {
            throw 'error'
        }
        user.pageViews.push(savedPageView) 
        // save the modified savedUser
        UserRepository.save(user , function(error, savedUser){

        })

     })

   }

它使用 Repository 模式作为数据库层的抽象(与 Spring 应用程序中的 Repository 模式相同)。 基本上它通过传入令牌(来自 http req 对象)找到用户。如果找到用户,则更新用户实体并添加保存的 pageView 实体并保存修改后的用户。如果未通过令牌找到用户,则它会创建一个新用户,使用保存的 pageView 更新用户,保存用户。

如何在 Spring Project Reactor (Flux) 中编写相同的代码?

不使用block()是否可以解决这个问题?理想情况下,我想要一个不使用 block() 的解决方案。

首先,如果令牌不存在,您有一些生成令牌的逻辑。例如:

private Mono<String> getToken(String token) {
     return Mono
         .just(token)
         .switchIfEmpty(Mono.just("some token"));
}

在这种情况下,为此使用 switchIfEmpty 有点矫枉过正,但我​​认为您生成令牌的过程有点复杂,否则您可以使用 Optional<String>取而代之(例如 token.orElse("some token"))。

此外,我们还有一些逻辑可以通过令牌找到用户,或者如果给定令牌没有用户则创建新用户:

private Mono<User> findUserByToken(String token) {
    return userRepository
        .findByToken(token)
        .switchIfEmpty(userRepository.save(new User("John Doe", token)));
}

现在我们有了这些方法,我们可以创建一个 PageView 并在整个过程中使用这些方法。我开始创建 PageView 的原因是因为这是整个令牌中的第一个 "constant",无论是否找到 token/user:

return Mono
    .just(new PageView(uri))
    .flatMap(pageViewRepository::save)
    .flatMap(pageView -> getToken(token)
        .flatMap(this::findUserByToken)
        .doOnNext(user -> user.setFoo("foo"))
        .doOnNext(user -> user.getPageView().add(pageView)))
    .flatMap(userRepository::save)
    .map(User::getToken);

现在,由于您需要将令牌添加到响应中,而且我发现令牌不知何故是 User 对象的一部分(否则 UserRepository.findByToken() 不起作用?),在最后使用 User::getToken 来检索要传递给响应的令牌会更容易。


请注意,存储库模式确实可以与 Spring 一起正常工作,但仅对 MongoDB、Cassandra、Couchbase 和 Redis 提供响应式支持。除此之外,还有通过 rdbc 对 PostgreSQL 的响应式支持,但我认为 Spring 数据不支持该功能。