【问题标题】:How to use Mono and Flux in handler functions with Spring WebFlux Reactive ways如何通过 Spring WebFlux Reactive 方式在处理程序函数中使用 Mono 和 Flux
【发布时间】:2024-01-24 08:08:01
【问题描述】:

检查-->

我的模型看起来像这样。

@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 */

这是我的处理程序类来处理我的功能端点。

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());
    }
}

问题: 什么是响应式编码getMemberDetails 并重用之前编写的函数getUserDetails.

要求。

DESIRED FUNCTIONALITY : 获取会员详细信息功能。

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);

}

期望的行为

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

我的路由中的 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
    }
]

【问题讨论】:

    标签: java spring spring-webflux project-reactor java-10


    【解决方案1】:

    在我看来,在第一种方法中没有太多可重用的内容:由于这两种方法都返回完整格式的ServerResponse,因此您无法编写原件。这只是 repository.findById() 和一些样板将其转换为 ok 响应或 404 响应...

    因此,您真正需要的是一种在多次调用repository.findById 的基础上编写getMemberDetails 的方法。如果我正确解释了您的伪代码,您希望响应只是原始“用户”下所有“成员”的Flux&lt;PlanDetail&gt;(忽略有关原始用户的信息)?

    您应该能够通过 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);
    }
    

    这种方法的唯一警告是它不区分“未找到”(未找到原始用户)与“无内容”(用户没有可以找到的成员)。

    【讨论】:

      【解决方案2】:

      我认为这最终对我有用。如果我的伪代码不是很清楚,我很抱歉。感谢您的回答@Simon Baslé

          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);
          }
      

      【讨论】:

      • 你定义了 notFound,但没有使用它
      最近更新 更多