现代网络站点(web site)的主要任务是显示动态内容。从某些角度看, 就是指用户将输入信息发送给网络应用(web application)进行处理之后网络应用再将处理结果发送回用户。某些特别情况下,从用户角度看后端操作运行足够快并且一切正常。但是在有些时候,后端的处理往往会因为出现较多的时间消耗而引起延迟。这种延迟有可能过长而最终使用户认为是其自己的操作错误,他们也许会放弃当前的操作或重新提交请求。
处理操作运行周期的事件过长并不是一个新问题。Java提供的健壮的线程机制能够建立起后台的任务分配。另外,随着EJB 2.0规范的出现,基于消息的EJB(简称MDB)能够被用来执行后台操作。不过请记住,这些机制是为了处理异步操作而设计的。从你启动了一个线程或后台处理,到某段时间之后你被通知或者是需要查看结果,整个过程完全是异步的。
对于轻型的长时间运行的一般同步应用仍然会引起大量处理的问题你有何看法?想象一下,一个音乐爱好者登陆她喜欢的网站为一场刚开始售票的演出订票(我想起了最近Bruce Springsteen的演唱会)。在通常情况下,网站会很好的执行并且我们的音乐爱好者能够以她自己的方式买到票。可是,当负载很大时,服务器便会慢下来,使该用户的操作不能顺利进行(用户会以为自己的订购操作失败了),因此她会接二连三的点击"提交"按钮。不幸的是,每一次点击"提交"都把之前的订票操作中止了。
有很多方法处理这种情况。最显而易见的方法是防止用户重复提交相同请求。另外也可以跟踪用户先前提交的请求并回复先前的提交动作。下图显示了一个简单的服务端小程序(servlet)的输出数据,该程序处理每一个收到的请求,为每一个请求分配一个票号。
处理简单的提交任务
图1:简单提交任务处理
最主要和最有效解决多重提交问题的方法就是防止这种情况发生。下面列出的HTML程序ConcertTickets.html是一个简单的表单,用于获取由用户输入的音乐会名字并提交给服务端小程序(servlet)订票。当网站相应迅速时处理执行的很好。但是,如果网站处于性能很低的状态并且提交的任务处理不够快,用户认为失败后会重新提交。处理过程在表1下的图2中显示。
表1:ConcertTickets.html
01: <html>
02: <head><title>Online Concert Tickets</title></head>
03:
04: <center><h1>Order Tickets</h1></center>
05:
06: <form name="Order" action="./SimpleOrder" method="GET">
07: <table border="2" width="50%" align="center" bgcolor="CCCCCC">
08: <tr><td align="right" width="40%">Concert: </td>
09: <td width="60%"><input type="text" name="Concert" value=""></td></tr>
10:
11: <tr><td colspan="2" align="center">
12: <input type="submit" name="btnSubmit"
13: value="Do Submit"></td></tr>
14: </table>
15: </form>
16: </body>
17: </html>
图2:重复的任务提交
防止多重提交
最简单的解决多重提交问题的方法是防止这种情况发生。下面是我们表1中表单程序的一个修改,加入了少量的javascript脚本。内嵌的javascript脚本记录以前“提交”按钮被点击的次数。在用户再次提交时,将会弹出报警窗口并且表单不会再被提交。我们能够通过给“提交”按钮加入onClick事件属性来缩短普通提交处理过程的周期。每次提交按钮被点击时,onClick的代码就会执行。在我们这个例子中,会引起javascript脚本函数checksubmitcount()被调用。但仅仅调用一个函数并不会真正起到作用。如果我们只是加入onClick,每次提交按钮被点击时我们会收到弹出的警报,而任务提交也会立即发生。用户会被警告她操作错误,但是请求还是会被提交。这样做仅仅对用户端有了一定的改善。而在服务端结果是一样的:多重提交。
表2:Concert2.html
01: <html>
. . .<!?与表1 :ConcertTickets.html程序02~11相同 //-->
12: <input type="button" name="btnSubmit"
13: value="Do Submit"
14: onClick="checksubmitcount();"></td></tr>
15: </table>
16: </form>
17:
18: <script language="javascript">
19: <!--
20: var submitcount = 0;
21: function checksubmitcount()
22: {
23: submitcount++;
24: if (1 == submitcount )
25: {
26: document.Order.submit();
27: }
28: else
29: {
30: if ( 2 == submitcount)
31: alert("You have already submitted this form");
32: else
33: alert("You have submitted this form"
34: + submitcount.toString()
35: + " times already");
36: }
37: }
38: //-->
39: </script>
40: </body>
41: </html>
我们能通过更进一步和更精细的改变我们网页的工作方式来解决问题。敏锐的读者可能会注意到对表单添加的改变。程序第12行定义的按钮类型原先为”submit”,现在改成了”button”。而网页界面是完全相同的。可是,与表单相关的默认动作(程序第6行,调用服务端小程序)不再是自动执行的了。我们现在能够通过程序控制表单向服务器端的提交,我们的问题也得到了解决,不是么?
表2当然是一种改善,但我们还是需要一些其他方法。仍然有许多情况会导 致错误。如果用户按下了浏览器的后退键或者刷新了整个网页会怎么样?如果用户的浏览器关闭了javascript的功能或者浏览器不能处理会如何呢?我们还是可以处理这个问题的,但是为了取代防止多重提交,我们需要在后端通过表单处理的服务端小程序处理它们。
为了理解如何解决多重提交问题,我们必须首先理解服务端小程序会话(sessions)机制。每个人都知道,HTTP协议的固有性质中并不对状态(客户端请求信息的历史记录)进行记录。为了处理状态,我们需要一些方法使浏览器能够将当前请求与其他大量请求联系起来。会话(session)程序提供给我们一个解决这个问题的方案。HttpServlet中的方法doGet()和doPost()使用了两个指定的参数:HttpServletRequest和HttpServletResponse。服务端小程序请求参数使我们能够访问会话(session)。会话为访问和存储状态提供了机制。
什么才是会话(session)呢?会话(session)包括很多内容:
状态集??由web服务器管理并且由特定用户所有请求所共享的详细表示描述。
存储空间??通过HttpSession接口至少将HttpServlets所需状态数据和定义存储起来。
在我们具体了解如何使用服务器端方案解决我们的问题之前,我们还需要了解服务端小程序会话(session)的生命周期。与EJB及其他服务端实体一样,会话在生存期中通过一个定义的状态集运行。下图显示了会话的生命周期。Servlet可在三种特定的状态中转换:不存在(does not exist),新建(new),非新建(not new/或使用中in-use)。
图3:服务端小程序会话(session)生命周期
[ 相关贴图 ]
a) 会话在开始时处于不存在状态。会话从这一状态开始或者由于许多原因而返回到此状态。最主要的原因就是用户以前没有访问过这些状态或者是由于用户脱离(超时)站点或退出使会话被设置为无效。
b) 当会话被建立时便会从“不存在”状态进入“新建”状态。新建与非新建状态的区分是非常重要的,因为HTTP协议不记录状态信息。根据servlet详细说明书描述,在客户端返回会话给服务端之前会话不能够进入非新建状态(即从预期会话转变为当前会话)。这样在客户端不知道或者还没有决定加入会话时会话处于新建状态。
c) 当会话通过cookie或是重写URL()返回到服务器时,会话就变为“使用中”或“非新建”状态。
d) 通过各种get与set方法继续使用会话会使其维持在“使用中”状态。
e) 当会话由于长时间没有被使用而超时或显式的被设为无效则会发生图中所示的5以及6所标识的转移。不同应用服务器用不同方式处理超时。BEA公司的WebLogic使应用部署者能够通过与web应用一起打包的特殊部署描述脚本(weblogic.xml)设置会话超时的时限。
现在我们了解了会话的生命周期,那么如何获得一个会话并有效的使用它呢?接口HttpServletRequest提供了两个关于会话的方法:
public HttpSession getSession()返回一个新的会话或一个已存在的会话。
如果提供一个有效的会话ID(可能是通过cookie)则返回一个存在的会话。返回新的会话可能会有许多原因:用户最初的会话(无法提供有效会话ID);会话超过有效时间(提供了会话ID);一个无效的会话(提供了会话ID);或者是明确指出会话无效(提供了会话ID)。
public HttpSession getSession(boolean)可能返回新会话、存在的会话或者空。getSession(true)尽可能返回一个存在的会话。否则创建一个新会话。getSession(false) 尽可能返回一个存在的会话否则返回空。
我们还是只解决了手边一半的问题。我们希望能够跳过会话“新建”状态并自动的转换到会话“使用中”状态。我们能够通过重定向浏览器到处理服务端小程序自动的实现这些。表3把服务端小程序会话逻辑和重定向用户端与有效会话到处理服务端小程序的能力结合在一起。
表3:RedirectServlet.java
01: package multiplesubmits;
02:
03: import java.io.*;
04: import java.util.Date;
05: import javax.servlet.*;
06: import javax.servlet.http.*;
07:
08: public class RedirectServlet extends HttpServlet{
09: public void doGet (HttpServletRequest req, HttpServletResponse res)
10: throws ServletException, IOException {
11: HttpSession session = req.getSession(false);
12: System.out.println("");
13: System.out.println("-------------------------------------");
14: System.out.println("SessionServlet::doGet");
15: System.out.println("Session requested ID in Request:" +
16: req.getRequestedSessionId());
17: if ( null == req.getRequestedSessionId() ) {
18: System.out.println("No session ID, first call,
creating new session and forwarding");
19: session = req.getSession(true);
20: System.out.println("Generated session ID in Request: " +
21: session.getId());
22: String encodedURL = res.encodeURL("/RedirectServlet");
23: System.out.println("res.encodeURL(\"/RedirectServlet\");="
+encodedURL);
24: res.sendRedirect(encodedURL);
25: //
26: // RequestDispatcher rd = getServletContext().getRequestDispatcher(encodedURL);
27: // rd.forward(req,res);
28: //
29: return;
30: }
31: else {
32: System.out.println("Session id = " +
req.getRequestedSessionId() );
33: System.out.println("No redirect required");
34: }
35:
36: HandleRequest(req,res);
37: System.out.println("SessionServlet::doGet returning");
38: System.out.println("------------------------------------");
39: return;
40: }
41:
42: void HandleRequest(HttpServletRequest req, HttpServletResponse res)
43: throws IOException {
44: System.out.println("SessionServlet::HandleRequest called");
45: res.setContentType("text/html");
46: PrintWriter out = res.getWriter();
47: Date date = new Date();
48: out.println("<html>");
49: out.println("<head><title>Ticket Confirmation</title></head>");
50: out.println("<body>");
51: out.println("<h1>The Current Date And Time Is:</h1><br>");
52: out.println("<h3>" + date.toString() + "</h3>");
53: out.println("</body>");
54: out.println("</html>");
55: System.out.println("SessionServlet::HandleRequest returning");
56: return;
57: }
58: }
这如何解决我们的问题的呢?测试上面这段代码显示出在11行我们尝试获得一个会话的句柄。在17行我们通过检测为空的会话ID或检测有效会话ID来确定存在一个有效的会话。如果不存在会话就执行18-29行程序。通过下述方法我们处理多重提交的问题,在19行首先建立一个会话,在22行使用URL编码添加新会话ID,并且在24行重定向我们的服务端小程序到新的URL编码。
不熟悉重写URL的读者可参考15行到23行。一个HttpServlet对象可以重写URL。这个过程将一个会话ID插入到URL。底层的应用服务器能够自动的用编码URL提供给服务端小程序或JSP一个存在的会话。由于这依赖于应用服务器,为了使上面的例子可以运行,你可能需要设置环境使URL能够重写!
总结
在这篇文章中,我们讨论了多重提交问题的许多解决方案。每一个方案都有优点和缺陷。在处理问题时,要清晰的理解和权衡解决方案多方面的优点和缺陷。我们最后的例子有利于解决客户端额外重复的访问和浏览的问题。javascript脚本的方法是最好的,但是需要客户端支持才能够运行。和其他任何问题一样,会有一大堆解决方案,每个方案都会有其自己的优缺点。掌握每个方案的优缺点,有利于我们为解决问题作出最好的选择。
分享到:
相关推荐
通过上述分析可知,解决JSP页面提交乱码问题的关键在于确保客户端与服务器端之间字符编码的一致性。具体可以通过设置HTML页面编码、设置JSP页面处理请求的字符编码以及正确处理不同的表单提交方式等方法来实现。此外...
在JSP(Java Server Pages)中处理中文字符时,特别是在用户通过表单提交数据时,可能会遇到各种编码问题。这是因为Web应用中涉及到多种编码格式,包括浏览器编码、HTTP请求编码、服务器编码等,如果这些编码不一致...
在JavaServer Pages (JSP) 技术中,创建一个包含多个提交按钮的页面是常见的需求,每个按钮可能对应不同的处理逻辑,比如提交到不同的后端Servlet或执行不同的操作。本示例着重讲解如何在JSP页面中实现多个提交按钮...
【标题】"jsp网站流量图表分析系统"是一个基于JavaServer Pages (JSP)技术构建的系统,用于收集、处理和展示网站流量数据。这个系统旨在帮助网站管理员深入了解用户行为,通过对访问量、页面浏览量、用户来源等关键...
【JSP开发实例需求分析】 JSP(Java Server Pages)是一种由Sun Microsystems公司主导的动态网页技术,它基于Java语言,旨在实现动态内容与静态页面的分离,提供了一种高效的方式来创建交互式Web应用程序。JSP的...
`request`对象用于获取用户提交的数据,而`response`则负责将处理结果返回给客户端。 流量数据通常存储在数据库中,如MySQL或Oracle。因此,我们需要连接到数据库,这可以通过JDBC(Java Database Connectivity)...
【标题】"jsp课程作业提交系统"是一款专为教学环境设计的应用程序,它利用Java Server Pages(JSP)技术来实现学生作业的在线提交、管理与评估。此系统旨在简化教师对作业的收集和批改过程,同时也方便学生查看作业...
### JSP重复提交问题及其解决方法 #### 一、引言 在Web应用程序开发中,尤其是在使用Java Server Pages (JSP)技术时,一个常见的问题是重复提交数据。当用户不小心刷新了页面或按下了浏览器的“后退”按钮时,可能...
一个Jsp两个ActionForm分别提交.rar一个Jsp两个ActionForm分别提交.rar一个Jsp两个ActionForm分别提交.rar一个Jsp两个ActionForm分别提交.rar一个Jsp两个ActionForm分别提交.rar
**JSP论文提交系统** **一、JSP技术概述** JSP(JavaServer Pages)是Java平台上的一个标准,用于创建动态网页。它允许开发者在HTML、XML或其他标记语言中嵌入Java代码,使得服务器端能够处理数据并生成动态内容。...
"JSP试卷分析系统"是一个基于Web的教育技术应用,主要设计用于帮助教师或教育机构进行试卷的管理和分析。这个系统采用Java语言开发,利用JSP(JavaServer Pages)技术来构建前端界面,并且可能结合了Servlet和...
在网页开发中,JSP(JavaServer Pages)...总的来说,隐藏JSP GET提交时地址栏中的问号涉及到URL设计、请求处理方式以及可能的前端与后端交互策略。理解并灵活运用这些技术,能帮助开发者构建更安全、更美观的Web应用。
总的来说,掌握使用JSP处理用户注册和登录的能力,不仅有助于构建功能完善的Web应用,还能加深对Web开发流程和服务器端编程的理解。在实际项目中,还需要考虑更多的安全措施,如加密密码、防止SQL注入、XSS攻击等,...
提交表单后,JSP页面接收到请求,从中提取文件数据。这里涉及的关键技术包括解析multipart/form-data编码的HTTP请求,这是文件上传请求的标准格式。 接下来,我们将分析提供的文件名称列表: 1. `Upload.jsp`: 这很...
验证jsp提交
在Web开发中,尤其是使用JavaServer Pages (JSP) 技术时,页面表单的重复提交是一个常见的问题。这可能会导致数据不一致或者服务端处理逻辑错误。本篇文章将探讨如何有效地防止JSP页面中的表单重复提交,确保系统的...
例如,用户在提交论文时,JSP页面会收集用户输入的信息,如论文标题、作者、摘要等,同时处理文件上传,将论文文档存储到服务器的指定位置。 Java后端处理系统的部分,通常包括Servlet、Model、Controller和DAO...
JSP 表单处理 JSP 表单处理是指在 JSP 编程中,通过设计网页上的表单,收集用户输入的数据信息,并对其进行处理的技术。本章节将详细介绍利用 JSP 的 Request 对象的 getParameter 方法来获取表单数据的方法,并对 ...
对于作业提交功能,可能采用了表单提交的方式,利用JSP的request对象获取用户输入的数据,然后通过Servlet或控制器组件(如Spring MVC的Controller)处理这些数据,将其存储到数据库中。数据库设计是系统的关键部分...
综上所述,`Ext+JSP`的组合提供了一种有效的方法来处理Web应用中的数据提交。通过前端`Ext`的交互性和数据管理,配合后端`JSP`的业务处理能力,能够构建出功能强大且用户体验良好的Web应用。在实际项目中,开发者...