【问题标题】:Spring Controller interprets checkboxes wrongly after two submits两次提交后,Spring Controller 错误地解释了复选框
【发布时间】:2021-04-02 14:25:34
【问题描述】:

免责声明: 我在编码方面不是很有经验,并且大部分都是自己学习的。 这可能有一个非常简单的答案。


我有一个由两个复选框 (A) 和 (B) 组成的 html 表单,它们提交给一个弹簧控制器。控制器验证两个复选框都已设置。对于每个未设置的复选框,BindingResult 都会设置一个错误。如果未设置任何复选框,则重新加载页面并显示绑定结果错误。

我的问题:以下两个选中不同框的提交可以被检测为“都已被选中”。

复制

首次提交

  • 仅选中两个复选框中的一个复选框 (A)
  • 在 Chrome 中使用 F12 调试编辑器删除另一个复选框 (B)
  • 提交

结果:浏览器网络分析选项卡显示只有一个复选框 (A) 按预期提交。 因此,在控制器中,复选框 (A) 的值为 true,而缺少的复选框 (B) 的值为 false。到目前为止一切顺利。

第二次提交 通过以下步骤,我得到了所谓的问题:

  • 选中之前删除的复选框 (B)
  • 在 Chrome 中使用 F12 调试编辑器删除之前选中的复选框 (A)
  • 提交

结果:在控制器中,复选框 (A) 和 (B) 的值都为真,而我希望只有 (B) 为真

简化代码

html 表单

<form th:action="@{${URL_FEEDBACK_START}}" th:field="${participant}" th:object="${participant}" method="post">
    <input type="hidden" th:field="*{project}" />
    <input type="checkbox" id="priceGameAccepted" th:field="*{priceGameAccepted}" class="form-check-input" required="required" />
    <input type="checkbox" id="dataPrivacyAccepted" th:field="*{dataPrivacyAccepted}" class="form-check-input" required="required" />
    <div class="form-group mb-0">
        <div>
            <button type="submit" id="submit" class="btn btn-primary">
                Zum Feedback!<br /><i class="fas fa-step-forward"></i>
            </button>
        </div>
    </div>
</form>

控制器

@PostMapping(path = "/submit")
public String submit(Model model,
        HttpSession session,
        @ModelAttribute(SessionAttributeHelper.PROJECT) Project project,
        @ModelAttribute(SessionAttributeHelper.PARTICIPANT) @Valid Participant participant,
        BindingResult bindingResult) {

    try {
        validateOptionalPriceGameAccepted(project, participant);
        validateDataPrivacyAccepted(participant);
        
            [... here would be the following code]

    } catch (DataPrivacyNotAcceptedException e) {
        bindingResult.addError(new FieldError("participant", "dataPrivacyAccepted", e.getMessage()));
        return backToForm(model, project);
        
    } catch (PriceGameNotAcceptedException e) {
        bindingResult.addError(new FieldError("participant", "priceGameAccepted", e.getMessage()));
        return backToForm(model, project);
    }
}

private String backToForm(Model model, Project project) {
    UiText uiText = uts.getUiText(project, UiTextKey.MSG_FEEDBACK_START);
    mfs.fillUiText(model, uiText); //mfs = ModelFillerService
    mfs.fillGlobal(model);
    return ApplicationPathHelper.RES_FEEDBACK_START;        
}

ModelFillerService

public void fillGlobal(Model model) {
            
    LOG.debug("Preparing model for global area");
    model.addAttribute("URL_HOME", ApplicationPathHelper.URL_HOME);
    model.addAttribute("URL_PROJECTHOME", ApplicationPathHelper.URL_PROJECTHOME); //TODO where necessary?
    model.addAttribute("URL_IMPRESSUM", ApplicationPathHelper.URL_IMPRESSUM);
    model.addAttribute("URL_DATENSCHUTZ", ApplicationPathHelper.URL_DATENSCHUTZ);
    model.addAttribute("URL_PRICEGAME", ApplicationPathHelper.URL_PRICEGAME);
    
    LOG.debug("Preparing model for feedback start area");
    model.addAttribute("URL_FEEDBACK_START", ApplicationPathHelper.URL_FEEDBACK_START);
    model.addAttribute("URL_FEEDBACK_QUESTION_SUBMIT", ApplicationPathHelper.URL_FEEDBACK_QUESTION_SUBMIT);
    model.addAttribute("URL_FEEDBACK_RESULT_SUBMIT", ApplicationPathHelper.URL_FEEDBACK_RESULT_SUBMIT);
    
    LOG.debug("Preparing model for anonymous area");
    model.addAttribute("URL_LOGIN_FORM", ApplicationPathHelper.URL_LOGIN_FORM);
    model.addAttribute("URL_LOGIN", ApplicationPathHelper.URL_LOGIN);
    
    LOG.debug("Preparing model for administration area");
    model.addAttribute("URL_ADMIN_PROJECTDETAILS", ApplicationPathHelper.URL_ADMIN_PROJECTDETAILS);
    model.addAttribute("URL_ADMIN_PROJECTS", ApplicationPathHelper.URL_ADMIN_PROJECTS);
    model.addAttribute("URL_ADMIN_SHOWFEEDBACK", ApplicationPathHelper.URL_ADMIN_SHOWFEEDBACK); //TODO notwendig?
    model.addAttribute("URL_ADMIN_EDITUITEXT", ApplicationPathHelper.URL_ADMIN_EDITUITEXT); //TODO notwendig?
    model.addAttribute("URL_ADMIN_SHOWUSERAGENTS", ApplicationPathHelper.URL_ADMIN_SHOWUSERAGENTS); //TODO notwendig?
    model.addAttribute("URL_LOGOUT", ApplicationPathHelper.URL_LOGOUT);
    
    model.addAttribute("TIMENOW", ZonedDateTimeHelper.nice(ZonedDateTimeHelper.nowCET()));      
    
    /*
     * URLs for DEV profile
     */
    List<String> profiles = Arrays.asList(environment.getActiveProfiles());

    if (profiles.contains(ApplicationProfileHelper.DEV_PROFILE)) {
        LOG.debug("Preparing model for DEV profile.");
        model.addAttribute("isDevProfile", true);
    }
    
    if (profiles.contains(ApplicationProfileHelper.REV_PROFILE)) {
        LOG.debug("Preparing model for REV profile.");
        model.addAttribute("isRevProfile", true);
    }
}

public void fillUiText(Model model, UiText uiText) {
    model.addAttribute(uiText.getUiTextKey().toString(), uiText.getText());
}

验证方法

private void validatePriceGameAccepted(Project project, Participant participant) throws PriceGameNotAcceptedException {
    boolean priceGameStatementAccepted = participant.isPriceGameAccepted();
    if(project.isPricegame() && ! priceGameStatementAccepted) {
        throw new PriceGameNotAcceptedException();
    }       
}

private void validateDataPrivacyAccepted(@Valid Participant participant) throws DataPrivacyNotAcceptedException {
    boolean dataPrivacyStatementAccepted = participant.isDataPrivacyAccepted();
    if(! dataPrivacyStatementAccepted) {
        throw new DataPrivacyNotAcceptedException();
    }
}

软件 带有 Spring 和 Thymeleaf 模板引擎的 Java 11,这可能不是这里的问题。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.1</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

想法

是否有一些我看不到或不理解的 Spring Web 或数据功能?

【问题讨论】:

  • 能否添加backToForm方法的内容?
  • @Zack 我按要求添加了代码。

标签: java html spring forms checkbox


【解决方案1】:

我应该提供类头...

使用这个

@SessionAttributes(names = {"participant", "project"})
public class MyClass {

结合这个

@PostMapping(path = "/submit")
public String submit(Model model,
        HttpSession session,
        @ModelAttribute(SessionAttributeHelper.PROJECT) Project project,
        @ModelAttribute(SessionAttributeHelper.PARTICIPANT) @Valid Participant participant,
        BindingResult bindingResult) {

显然将参与者存储在会话中并从那里加载回来。我不太清楚为什么在以下两次提交中这些对象会被合并。

最终的解决方案是从@ModelAttribute 和@SessionAttribute 中移除参与者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2014-08-27
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 2021-10-29
    • 2016-03-14
    相关资源
    最近更新 更多