liferay防止form表单重复提交

有时候我们会遇到form表单重复提交的问题,例如双击提交,或者是刷新浏览器。这种时候就会出现form被重复提交的情况。那么我根据我所知道的情况对liferay防止表单重复提交进行一下总结:

1.

在liferay-portlet.xml中设置<action-url-redirect>true</action-url-redirect>

这种方式简单暴力,直接解决了重复提交。但是问题也随之而来,由于是重定向,那么request的生命周期就已经结束了。我们也就无法使用request.setAttribute();无法从action传递复杂对象到JSP。

在liferay的action里面想控制跳转 可以使用

mvcPortlet actionURL跳转问题

中所提到的方法来控制。但是response.setParameter只能传递字符串和字符串数组。没法传递复杂对象。

2.

不配置<action-url-redirect>true</action-url-redirect>

而是在action的方法里面手写重定向,这种方式更加灵活,在只是执行查询的时候 可以不做重定向,这样就可以使用request.setAttribute();

但是当做增删改的时候 由于仍需要重定向,所以还是有无法传递复杂对象的问题。

3.

那我就是任性,当我使用了重定向时候 还是想传递复杂对象怎么办?

那也可以实现。我们可以通过portletSession.setAttribute();方法来传值。但是这种办法虽然解决了传值问题,也带来了新的问题。那就是服务器内存开销大,当我们使用集群的时候,可能在session复制的时候带来更巨大的开销。包括时间上的开销和空间上的开销。

4.

让我们看看liferay的源码是怎么做的。

我以前的时候一直很纳闷,liferay这么大的项目 这么多年了。为什么代码写的这么不美观,这么恶心。总是在jsp里面写java块。而且经常在jsp里面去调用service层的方法。我跟踪了一些liferay的源码。例如liferay的用户管理。查询用户 并展示列表。可提供翻页,并且每条记录都有链接可以修改用户。

当修改用户时候,我们进入用户详细信息页面,点击保存的时候,会去后台保存,并返回到详细页面(这里liferay并没有返回到列表页面)。当点击取消按钮时会返回到列表页面,并且还在翻页所在的页数。

但是用户信息在列表页面里面还是被刷新出来了,我们看到的值是最新的。

我又仔细看了一下源码,发现点击进入用户详细信息的页面的链接里面 liferay拼接了列表页面上的查询条件,还有翻页的页码信息。

并把这个URL放在了取消按钮上。所以当点击这个按钮的时候 就会回到之前看到的列表页面上。

那么他的刷新列表记录的值是怎么实现的呢?

我看了他的jsp文件 发现其调用service层的查询方法正是在jsp里面写的。先用ParamUtil.getXxx(); 方法取得所有参数,然后用这个参数去调用service方法做展示。

综上所述,我就理解了为什么liferay的页面里面经常出现java代码。其实就是为了能够在重定向的时候 避开传递复杂类型数据的困难。

所以我建议为了防止重复提交,可以使用不配置

<action-url-redirect>true</action-url-redirect>

然后通过在action方法里面手写重定向的方法来实现防止重复提交。

手写重定向代码如下:

ThemeDisplay themeDisplay = (ThemeDisplay) actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
String portletName = (String)actionRequest.getAttribute(WebKeys.PORTLET_ID);
PortletURL redirectURL = PortletURLFactoryUtil.create(PortalUtil.getHttpServletRequest(actionRequest), portletName,
themeDisplay.getLayout().getPlid(), PortletRequest.RENDER_PHASE);

redirectURL.setParameter("view", "abc");

actionResponse.sendRedirect(redirectURL.toString());

==============
经过我在liferay的官网上的搜索,发现了更好的实现方式,那就是当我们需要从页面跳转到页面,携带一些参数并在目标页面利用携带的参数查询的时候,我们可以使用
1. renderURL来直接跳转到页面,不在action写方法,这样查询需要写在JSP里面。
2. 使用actionURL请求后台方法,由后后台重定向到页面,但是由于这样是从action阶段到render阶段。actionrequest里面携带的值会被清空,我们需要在action的方法里面使用
String value = ParamUtil.getString(actionRequest,”parameter-name”);
actionResponse.setRenderParameter(“parameter-name”, value);
的方式来一个一个重新取和存,然后在JSP中使用renderRequest.getParameter(“parameter-name”);来获取值。那么这么枯燥的步骤是否有更好的替代方案呢?
当我们使用的是liferay的MVC的时候 我们可以在portlet.xml中做如下配置

<init-param>
    <name>copy-request-parameters</name>
    <value>true</value>
</init-param>

这样liferay就会自动帮我们把actionRequest里面的所有parameter复制到renderRequest之中。

3.通过使用actionURL请求action里面的方法,进行查询并将结果集传递到页面(这个才是重点)

我们之前说到使用portletSession 问题就是开销过大,其实最主要的原因就是猿们经常向session里面塞数据 但是忘记清除。liferay为我们提供了SessionMessage和SessionError类和对应的标签

<liferay-ui:success key=”success” message=”Greeting saved successfully!” />

<liferay-ui:error key=”error” message=”Sorry, an error prevented saving your greeting” />

来在JSP中展示,这个标签的话 同一个message只会展示一次。

SessionMessage同时也有存储复杂对象的方法和取方法,但是我还不清楚有没有自动删除,如果没有的话需要手动调用clear方法。这个待确认。

这样的话 用户F5之后 会没有可展示的数据!

建议配置重定向,查询在JSP中调用service同时配合

 

<init-param>
    <name>copy-request-parameters</name>
    <value>true</value>
</init-param>