2017-08-30 9 views
0

Je suis nouveau au printemps réacteur, donc je veux simplement factoriser ce printemps méthode des données (sur Kotlin):Spring WebFlux: Réactif MongoDB

fun save(user: User): Mono<User> { 
    if (findByEmail(user.email).block() != null) { 
     throw UserAlreadyExistsException() 
    } 

    user.password = passwordEncoder.encode(user.password) 
    return userRepository.save(user) 
} 

Merci

+0

Vous devez expliquer ce que votre problème est – s1m0nw1

+0

@ s1m0nw1 je veux factoriser ce style réactif – maystrovyy

Répondre

0

Quelque chose comme cela devrait fonctionner:

open fun save(req: ServerRequest): Mono<ServerResponse> { 
    logger.info { "${req.method()} ${req.path()}" } 
    return req.bodyToMono<User>().flatMap { 
     // You might need to "work out" this if since I don't know what you are doing 
     if (null != findByEmail(it.email).block()) { 
     throw UserAlreadyExistsException() 
     } 
     it.password = passwordEncoder.encode(it.password) 
     repository.save(it).flatMap { 
     logger.debug { "Entity saved successfully! Result: $it" } 
     ServerResponse.created(URI.create("${req.path()}/${it.id}")).build() 
     } 
    } 
    } 

Remarque J'utilise MicroUtils/kotlin-logging. Supprimez les instructions de journal si vous ne les connaissez pas ou si vous ne les souhaitez pas.

Fondamentalement, vous avez besoin de « consommer » (abonnez-vous a.k.a.) premier à ce qui arrive dans le ServerRequest afin d'accéder au contenu. Alternativement, au lieu de lancer une exception, vous pouvez également avoir un flux réel qui gère ce scénario; quelque chose comme:

open fun ... 
    return ServerResponse.ok() 
     // Keep doing stuff here...if something is wrong 
     .switchIfEmpty(ServerResponse.notFound().build()) 
} 

Vous pouvez régler l'exemple à votre type User au cas où vous voulez vraiment passer au lieu d'un ServerRequest.

0

(pardonnez-moi si la syntaxe Kotlin ne va pas et si je fais les choses dans le style Java: o)

fun save(user: User): Mono<User> { 
    //we'll prepare several helpful Monos and finally combine them. 
    //as long as we don't subscribe to them, nothing happens. 

    //first we want to short-circuit if the user is found (by email). 
    //the mono below will onError in that case, or be empty 
    Mono<User> failExistingUser = findByEmail(user.email) 
     .map(u -> { throw new UserAlreadyExistsException(); }); 

    //later we'll need to encode the password. This is likely to be 
    //a blocking call that takes some time, so we isolate that call 
    //in a Mono that executes on the Elastic Scheduler. Note this 
    //does not execute immediately, since it's not subscribed to yet... 
    Mono<String> encodedPassword = Mono 
     .fromCallable(() -> passwordEncoder.encode(user.password)) 
     .subscribeOn(Schedulers.elastic()); 

    //lastly the save part. We want to combine the original User with 
    //the result of the encoded password. 
    Mono<User> saveUser = user.toMono() //this is a Kotlin extension 
     .and(encodedPassword, (u, p) -> { 
      u.password = p; 
      return u; 
     }) 
     //Once this is done and the user has been updated, save it 
     .flatMap(updatedUser -> userRepository.save(updatedUser)); 

    //saveUser above is now a Mono that represents the completion of 
    //password encoding, user update and DB save. 

    //what we return is a combination of our first and last Monos. 
    //when something subscribes to this combination: 
    // - if the user is found, the combination errors 
    // - otherwise, it subscribes to saveUser, which triggers the rest of the process 
    return failExistingUser.switchIfEmpty(saveUser); 
} 

Une version abrégée sans variables intermédiaires ni commentaires:

fun save(user: User): Mono<User> { 
    return findByEmail(u.email) 
     .map(u -> { throw new UserAlreadyExistsException(); }) 
     .switchIfEmpty(user.toMono()) 
     .and(Mono.fromCallable(() -> passwordEncoder.encode(user.password)) 
       .subscribeOn(Schedulers.elastic()), 
      (u, p) -> { 
       u.password = p; 
       return u; 
      }) 
     .flatMap(updatedUser -> userRepository.save(updatedUser)); 
} 
0

Vous pouvez utiliser la fonction hasElement() en mode Mono. Jetez un oeil à cette fonction d'extension Mono:

inline fun <T> Mono<T>.errorIfEmpty(crossinline onError:() -> Throwable): Mono<T> { 
     return this.hasElement() 
       .flatMap { if (it) this else Mono.error(onError()) } 
} 

inline fun <T> Mono<T>.errorIfNotEmpty(crossinline onError: (T) -> Throwable): Mono<T> { 
    return this.hasElement() 
      .flatMap { if (it) Mono.error(onError.invoke(this.block()!!)) else this } 
} 

Le problème avec switchIfEmpty est qu'il évalue toujours l'expression passée dans l'argumentation - écrit ce code produira toujours objet Foo:

mono.switchIfEmpty(Foo()) 

Vous pouvez écrire votre propre extension à l'expression d'évaluation paresseuse passée en argument:

inline fun <T> Mono<T>.switchIfEmpty(crossinline default:() -> Mono<T>): Mono<T> { 
    return this.hasElement() 
      .flatMap { if (it) this else default() } 
} 

Voici deux autres fonctions d'extension - vous pouvez les utiliser pour vérifier si er mot de passe est correct:

inline fun <T> Mono<T>.errorIf(crossinline predicate: (T) -> Boolean, crossinline throwable: (T) -> Throwable): Mono<T> { 
    return this.flatMap { if (predicate(it)) Mono.error(throwable(it)) else Mono.just(it) } 
} 

inline fun <T> Mono<T>.errorIfNot(crossinline predicate: (T) -> Boolean, crossinline throwable: (T) -> Throwable): Mono<T> { 
    return this.errorIf(predicate = { !predicate(it) }, throwable = throwable) 
}