【问题标题】:How to get model attributes when using Spring WebFlux router functions?使用 Spring WebFlux 路由器功能时如何获取模型属性?
【发布时间】:2020-08-12 03:30:19
【问题描述】:

Spring WebFlux 提供两种编程选项可供选择:带注释的控制器功能端点。对于第一个,我们可以使用@ModelAttribute 注解将模型属性从控制器传递到视图(例如 Thymeleaf html-template),反之亦然。但是,当涉及到路由器功能时,到目前为止,我只是想出了如何将模型属性附加到ServerResponse,但找不到将它们取回的方法。考虑以下代码 sn-p:

@Configuration
public class StudentsRouterFunctions {

    // inject repository
    private final StudentsCrudRepository repo;

    public StudentsRouterFunctions(StudentsCrudRepository repo) {
        this.repo = repo;
    }

    @Bean
    RouterFunction<?> routs() {
        return RouterFunctions.route()
                .GET("/students", this::showStudents)
                .POST("/students", this::saveStudent)
                .build();
    }

    // #1: GET-handler
    private Mono<ServerResponse> showStudents(ServerRequest request) {

        // set model attributes
        Map<String, Object> model = new HashMap<>();
        Mono<Student> studentsList = repo.findAll().collectList();
        Mono<Student> newStudent = Mono.just(new Student());
        model.put("students", studentsList);
        model.put("studentForm", newStudent);

        // render the view
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_HTML)
                .render("students-template", model);
    }

    // #2: POST-handler
    private Mono<ServerResponse> saveStudent(ServerRequest request) {

        // and here I somehow need to get my new student object 
        // back from the view via "studentForm" model attribute

        // Student newStudent = request.getModel().get("studentForm"); 
        // !!! however, ServerRequest.getModel() method doesn't exist

        return repo.save(newStudent)
                .then(ServerResponse.status(HttpStatus.PERMANENT_REDIRECT)
                        .render("redirect:/students", new Object()));
    }

【问题讨论】:

    标签: java spring-boot thymeleaf spring-webflux project-reactor


    【解决方案1】:

    没有ServerRequest::getModel 方法,但ServerRequest::bodyToMono(Class) 可以将模型所在的主体提取到Mono&lt;T&gt;

    然后利用响应式存储库方法ReactiveCrudRepository::save的返回类型的优势,使用Mono::flatMap返回Mono&lt;T&gt;

    我还没有测试过,但它应该可以工作。

    private Mono<ServerResponse> saveStudent(ServerRequest request) {
        return request.bodyToMono(Student.class)               // Mono<Student> (a new one)
                      .flatMap(repo::save)                     // Mono<Student> (a saved one)
                      .then(ServerResponse                     // redirect sequence
                          .status(HttpStatus.PERMANENT_REDIRECT)
                          .render("redirect:/students", new Object()));
    }
    
    // .flatMap(repo::save) is the same as .flatMap(newStudent -> repo.save(newStudent))
    

    注意Mono::then 方法会丢弃源中的元素,因此重定向的对象仍为new Object(),因此您想使用Mono::map

    private Mono<ServerResponse> saveStudent(ServerRequest request) {
        return request.bodyToMono(Student.class)               // Mono<Student> (a new one)
                      .flatMap(repo::save)                     // Mono<Student> (a saved one)
                      .map(savedStudent -> ServerResponse      // redirect sequence
                          .status(HttpStatus.PERMANENT_REDIRECT)
                          .render("redirect:/students", savedStudent));
    }
    

    【讨论】:

    • 当我使用你的组合 bodyToMono.flatMap.map 时,我得到一个错误:不兼容的类型:推理变量 R 具有不兼容的边界等式约束:ServerResponse 下限:Mono,所以我改用bodyToMono.flatMap.flatMap 的组合。
    • 另外,bodyToMono() 在我的情况下不起作用,因为“studentForm”对象在视图中绑定到 Thymeleaf 的表单 th:object="${studentForm}" 属性,所以当提交表单时,我得到错误:bodyType=Student 不支持内容类型“application/x-www-form-urlencoded”。为了解决这个问题,我必须使用ServerRequest.formData() 方法而不是ServerRequest.bodyToMono(),然后手动将 MultiValueMap 反序列化为 Student 对象,然后再将其保存到存储库。
    • 除了那些cmets,我相信我可以接受你的回答。谢谢。
    • 我很高兴至少帮助您找到了可行的解决方案。
    猜你喜欢
    • 2018-09-22
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2014-04-03
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多