简体   繁体   English

如何通过Spring WebFlux Reactive方法在处理函数中使用Mono和Flux

[英]How to use Mono and Flux in handler functions with Spring WebFlux Reactive ways

Check for --> 检查-->

My model looks something like this. 我的模型看起来像这样。

@Document
public class PlanDetails {

    @Id
    private String id;
    private String name;
    private Double balance;
    private Double internet;
    private Date date;

    --> //String of id's basically.
    private List<String> members;

    public PlanDetails(){}

    public PlanDetails(String id, String name, Double balance, Double internet, Date date, List<String> members){
        this.id = id;
        this.name = name;
        this.balance = balance;
        this.internet = internet;
        this.date = date;
        this.members = members;
    }

    public String getId() {
        return id;
    }

    /* Getter setters ommited for brevity */

Here is my handler class to handle my functional endpoints. 这是我的处理程序类,用于处理我的功能端点。

package com.startelco.plandetailsapi.handler;

import com.startelco.plandetailsapi.model.PlanDetails;
import com.startelco.plandetailsapi.repository.PlanRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.springframework.http.MediaType.APPLICATION_JSON;
@Component
public class PlanDetailsHandler {

    private PlanRepository repository;


    public PlanDetailsHandler(PlanRepository repository){
        this.repository = repository;
    }

    //Get All Users
    public Mono<ServerResponse> getAllUsers(ServerRequest request){
        Flux<PlanDetails> users = repository.findAll();

        return ServerResponse.ok()
                .contentType(APPLICATION_JSON)
                .body(users,PlanDetails.class);
    }

    --> //Get User by ID
    public Mono<ServerResponse> getUserDetails(ServerRequest request){
        String id = request.pathVariable("id");

        Mono<PlanDetails> userMono = repository.findById(id);
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        return userMono.flatMap(user ->
                ServerResponse.ok()
                        .contentType(APPLICATION_JSON)
                        .body(BodyInserters.fromObject(user))
                        .switchIfEmpty(notFound)
                );
    }


    //Create a user
    public Mono<ServerResponse> saveUser(ServerRequest request){
        Mono<PlanDetails> userMono = request.bodyToMono(PlanDetails.class);

        return userMono.flatMap(user ->
                ServerResponse.status(HttpStatus.CREATED)
                        .contentType(APPLICATION_JSON)
                        .body(repository.save(user),PlanDetails.class)
                );
    }


    //Update user by ID
    public Mono<ServerResponse> updateUser(ServerRequest request) {
        String id = request.pathVariable("id");
        Mono<PlanDetails> existingUserMono = this.repository.findById(id);
        Mono<PlanDetails> userMono = request.bodyToMono(PlanDetails.class);

        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        return userMono.zipWith(existingUserMono,
                (user, existingUser) ->
                        new PlanDetails(existingUser.getId(), user.getName(), user.getBalance(), user.getInternet(),user.getDate(),user.getMembers())
        )
                .flatMap(user ->
                        ServerResponse.ok()
                                .contentType(APPLICATION_JSON)
                                .body(repository.save(user), PlanDetails.class)
                ).switchIfEmpty(notFound);
    }



    //Delete All Users
    public Mono<ServerResponse> deleteAllUsers(ServerRequest request) {
        return ServerResponse.ok()
                .build(repository.deleteAll());
    }
}

QUESTION: What is the Reactive way of coding getMemberDetails with reusing the previously written function getUserDetails. 问题:通过重用先前编写的函数getUserDetails. ,对getMemberDetails进行编码的反应性方法是什么getUserDetails. ?

Requirement. 需求。

DESIRED FUNCTIONALITY : Get Member Details Function. 所需功能:获取会员详细信息功能。

public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
   --> //Pseudocode
   String id = request.pathVariable("id");

   Mono<PlanDetails> userMono = repository.findById(id);
   Mono<ServerResponse> notFound = ServerResponse.notFound().build();

   if(userMono.member is not null){
       int length = userMono.member.length
       Flux<PlanDetails> memberFlux;
       for(int i=0;i<length;i++){
            Mono<PlanDetails> member = getUserDetails(id=userMono.member[i]);
            memberFlux.add(member);
       }
    }else{
       return ServerResponse.ok().build(memberFlux=null or empty array);
    }

    return ServerResponse.ok().build(memberFlux.flatmap);

}

DESIRED BEHAVIOR 期望的行为

{
    "id": "5b42ecc11cde674475cab39a",
    "name": "Alex Svirsky",
    "balance": 140,
    "internet": 20,
    "date": 1531107095659,
    "members": [
        "5b42ecdd1cde674475cab39b",
        "5b42ed421cde674475cab39c",
        "5b42ed5d1cde674475cab39d"
    ]
}

REST call in my routes is defined as http://localhost:8080/users/members/5b42ecc11cde674475cab39a 我的路由中的REST调用定义为http://localhost:8080/users/members/5b42ecc11cde674475cab39a

[    {
        "id": "5b42ecdd1cde674475cab39b",
        "name": "Bob Marley",
        "balance": 120,
        "internet": 9,
        "date": 1531107095559,
        "members": null
    },
    {
        "id": "5b42ed421cde674475cab39c",
        "name": "Charlie Sheen",
        "balance": 10,
        "internet": 9,
        "date": 1531107095555,
        "members": null
    },
    {
        "id": "5b42ed5d1cde674475cab39d",
        "name": "Dale Carnegie",
        "balance": 100,
        "internet": 9,
        "date": 1531107055555,
        "members": null
    }
]

In my opinion there isn't much that can be reused in that first method: as the two methods both return a fully-formed ServerResponse , you can't compose the originals. 在我看来,第一种方法没有太多可重复使用的:由于这两种方法都返回完整ServerResponse ,因此您无法撰写原始ServerResponse Which is merely repository.findById() with some boilerplate to transform that into an ok response or a 404 response... 这只是带有一些样板的repository.findById() ,可以将其转换为ok响应或404响应...

So what you actually need is a way to compose getMemberDetails on top of many calls to repository.findById . 因此,您实际需要的是一种在多次调用repository.findById getMemberDetails上构成getMemberDetails的方法。 If I interpret your pseudocode correctly, you want the response to just be a Flux<PlanDetail> of all the "members" under an original "user" (disregarding information about said original user)? 如果我正确解释了您的伪代码,您是否希望响应只是原始“用户”下所有“成员”的Flux<PlanDetail> (忽略有关所述原始用户的信息)?

You should be able to do that reactively with a flatMap : 您应该能够使用flatMap反应:

public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
  String id = request.pathVariable("id");

   Mono<PlanDetails> userMono = repository.findById(id);
   Mono<ServerResponse> notFound = ServerResponse.notFound().build();

   return userMono
       //transform from user mono to Flux of member details
       .flatMap(user -> Flux.fromArray(user.member)) //if `member` is an Iterable use the following instead:
       //.flatMapIterable(user -> user.member)
       //now we have a Flux of member IDs, go get details
       .flatMap(repository::findById)
       //this will naturally ignore not found members
       //if no member is found or array of IDs is empty, the main sequence is itself empty at this point
       .switchIfEmpty(notFound);
}

Only caveat of this approach is that it doesn't distinguish between "not found" (the original user is not found) vs "no content" (the user has no members that can be found). 这种方法的唯一警告是,它无法区分“未找到”(找不到原始用户)和“没有内容”(用户没有可以找到的成员)。

I think this works for me finally. 我认为这最终对我有用。 I am sorry if my pseudo code was not very clear. 很抱歉,我的伪代码不是很清楚。 Thanks for your answer @Simon Baslé 感谢您的答复@SimonBaslé

    public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
        String id = request.pathVariable("id");

        Mono<PlanDetails> userMono = repository.findById(id);
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        Flux<PlanDetails> userFlux = userMono.flatMapIterable(user -> user.getMembers()).flatMap(repository::findById);

        return ServerResponse.ok()
                .contentType(APPLICATION_JSON)
                .body(userFlux,PlanDetails.class);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM