用于防止双重提交的纯 Java/JSF 实现
Posted
技术标签:
【中文标题】用于防止双重提交的纯 Java/JSF 实现【英文标题】:Pure Java/JSF implementation for double submit prevention 【发布时间】:2017-02-15 03:51:23 【问题描述】:我们在 WebSphere v8.5 上使用 JSF 2.0 和几个组件库 PrimeFaces 4.0、Tomahawk 2.0、RichFaces 等。
我正在寻找通用机制来避免在刷新页面或再次单击提交按钮时重新提交表单。我有许多不同场景的应用程序。
目前我考虑过在onclick
属性中使用一段javascript 来禁用按钮,但这并不令人满意。为此,我正在寻找一个纯 Java 实现,例如 Struts2 <s:token>
。
【问题讨论】:
Ajax 是否解决了表单重新提交问题?因为只呈现页面的一部分,而不是整个页面或重定向 禁用 onClick 事件可能无法满足需要延迟计时器来完成此操作 是的@NassimMOUALEK 我实现了禁用按钮,直到 ajax 完成其渲染 这不完全是我想的问题,当用户点击多次,并且按钮上的速度非常快时,需要在提交之前延迟来决定是否应该提交 对,但我相信网页 JS 比人类快得多(一次又一次地点击)。 onclick 执行的第一个操作是禁用按钮,而不是 ajax 将在完成其循环时启用此按钮。因此,不会发生重叠事件 【参考方案1】:我正在寻找通用机制来避免页面刷新时重新提交表单
为此,至少有两种解决方案不能合并:
在同步发布后执行重定向。这样刷新只会重新执行重定向的 GET 请求而不是初始请求。缺点:您不能再利用请求范围向最终用户提供任何反馈。 JSF 2.0 通过提供新的flash scope 解决了这个问题。另见How to show faces message in the redirected page。
在后台异步执行 POST(使用 ajax)。这样刷新只会重新执行打开表单的初始 GET 请求。您只需要确保这些表单最初仅由 GET 请求打开,即您永远不应该通过 POST 执行页面到页面导航(无论如何,这本身已经是一个糟糕的设计)。另见When should I use h:outputLink instead of h:commandLink?
或者当再次点击提交按钮时
为此,基本上也有至少 2 种解决方案,如有必要,可以组合:
只需阻止最终用户在提交期间和/或成功提交后按下提交按钮即可。这有多种方法,都取决于具体的功能和设计要求。您可以在提交期间使用 JavaScript 禁用该按钮。您可以使用 JSF 的 disabled
或 rendered
属性在提交后禁用或隐藏按钮。另见How to do double-click prevention in JSF 2。您还可以在处理 ajax 请求期间使用覆盖窗口来阻止任何最终用户交互。 PrimeFaces 有<p:blockUI>
用于此目的。
在服务器端验证新添加实体的唯一性。如果您出于技术原因而不是出于功能原因绝对想避免重复,这将更加健壮。这相当简单:在有问题的 DB 列上放置 UNIQUE
约束。如果违反了这个约束,那么 DB(以及像 JPA 这样的 DB 交互框架)将抛出一个违反约束的异常。这最好与自定义 JSF 验证器结合使用,该验证器通过在该列上执行 SELECT
并检查是否没有返回记录来预先验证输入。 JSF 验证器允许您以友好面孔消息的形式显示问题。另请参阅Validate email format and uniqueness against DB。
【讨论】:
我在所有形式的应用程序中都使用<f:ajax />
,这个FlashScope
对redirect/reload
页面说。我该如何解决这个问题?
“另见”链接中有一个示例。
在服务器端验证唯一性可能会变得复杂。例如,我正在向我的朋友发送一条短信。在这种情况下,没有逻辑或约束来检查唯一性。也许,缓存和匹配请求会给你一个提示,但即使我们忽略匹配成本,也可能存在具有相同值的请求是规范的一部分的业务情况。【参考方案2】:
您可以使用 BalusC 的解决方案,而不是手动创建令牌。他在his blog提出了Post-Redirect-GET模式
可以在这些答案中找到替代解决方案:
Simple flow management in Post-Redirect-Get pattern How can Flash scope help in implementing the PostRedirectGet (PRG) pattern in JSF2.0【讨论】:
当我将 放在 h:commandButton 中时,现在它不会重新提交表单。是否完全满足要求? 问题是我们需要根据我们的场景启用和禁用双重提交。【参考方案3】:<!--Tag to show message given by bean class -->
<p:growl id="messages" />
<h:form>
<h:inputText a:placeholder="Enter Parent Organization Id" id="parent_org_id" value="#orgMaster.parentOrganization" requiredMessage="Parent org-id is required" />
<h:commandButton style="margin-bottom:8px;margin-top:5px;" class="btn btn-success btn-block " value="Save" type="submit" action="#orgMaster.save" onclick="resetform()" />
</h:form>
public String save()
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true); //This keeps the message even on reloading of page
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Your submission is successful.", " ")); // To show the message on clicking of submit button
return "organizationMaster?faces-redirect=true"; // to reload the page with resetting of all fields of the form.. here my page name is organizationMaster...you can write the name of form whose firlds you want to reset on submission
【讨论】:
这将打印成功消息并重置表单。不要忘记在前端的表单标签开始之前写以上是关于用于防止双重提交的纯 Java/JSF 实现的主要内容,如果未能解决你的问题,请参考以下文章