如果表单可以重复提交,会给服务器带来一些不必要的麻烦。也许,您会说,只要我们把提交的Action隐藏就可以了。例如在Struts中,只需要在Forward的后面加入一个属性redirect="true"就可以了。这个办法虽然看不见那个*.do了,但是别人可以使用后退,等退到刚才的页面,继续点提交,所以这方法不可行。
不过刚才在网上还看了一下资料,网上说有三中方法。
第一个是在javascipt中设置一个变量。
注意:这个disabled属性值在JS中应该是disabled或者" ",而在C#中,值才是true或者false,不要搞浑了哦!~
大家想想,这可都是javascript,可以说是在当前页面有效的。如果我提交以后,跳到别的页面,然后我在后退回来,页面还是要重新加载,那么我们的变量不就白设置。难道说这两种方法就不行了吗?不能这样说,既然人家写出来肯定有人家的道理,只不过我们用错地方了。您想一下,什么提交会在一个页面完成?对AJAX,所以说,这两个方法,只能应用杂AJAX提交中。
真正能解决问题的,当然就是我们今天的重点--->Token(令牌)
在Struts中,我们只需要调用几个方法,就可以完成。先来看看我是如何做的,然后我说下原理。
先来做一个Action(TokenAction),在这个Action中生成一个令牌。
提交进入LoginAction,在这个Action中做判断。
到这里,我们的提交问题就算解决了。怎么样,很简单吧。毕竟Struts已经给写好了,我们只需要调用就可以了。
不过需要注意的一点,在提交的时候,要用<html:form>,如果用一般的From标签,就跳不到提交成功页面(到底是为什么,马上就会讲到!)
看到这里,您会问了,我们刚才就调用了,那几个方法,可是内部怎样实现的我们一点都不知道啊!~别急,等我慢慢说来。
Step 1:
先来看看第一个方法saveToken()
public synchronized void saveToken(HttpServletRequest request) {
HttpSession session = request.getSession();
String token = generateToken(request);
if (token != null) {
session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token);
}
}
看来他是把值放入Session中了,而且命名为Globals.TRANSACTION_TOKEN_KEY
Step 2:
protected String renderToken() {
StringBuffer results = new StringBuffer();
HttpSession session = pageContext.getSession();
if (session != null) {
String token =
(String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
if (token != null) {
results.append("<input type=\"hidden\" name=\"");
results.append(Constants.TOKEN_KEY);
results.append("\" value=\"");
results.append(token);
if (this.isXhtml()) {
results.append("\" />");
} else {
results.append("\">");
}
}
}
return results.toString();
}
刚才,还记得,我在上面说到一个注意,说是要用<html:from>原因就在这第二步。因为当应用服务器初始化test4.jsp页面遇到标签<html:form>时,便会调用struts的FormTag类的renderToken()方法。
第二步的意思是:当检测到session中的Globals.TRANSACTION_TOKEN_KEY不为空时,在test4.jsp页面创建元素:
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">,
名称为:org.apache.struts.taglib.html.TOKEN就是Constants.TOKEN_KEY;
值为:session中的Globals.TRANSACTION_TOKEN_KEY的值,即为同步令牌值。
step 3:
取得session中的令牌值,然后resetToken,再从页面hidden元素取来令牌值,进行比较,如果相等则为第一次,不等则为重复提交。
public synchronized boolean isTokenValid(
HttpServletRequest request,
boolean reset) {
// Retrieve the current session for this request
HttpSession session = request.getSession(false);
if (session == null) {
return false;
}
// Retrieve the transaction token from this session, and
// reset it if requested
String saved = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
if (saved == null) {
return false;
}
if (reset) {
this.resetToken(request);
}
// Retrieve the transaction token included in this request
String token = request.getParameter(Constants.TOKEN_KEY);
if (token == null) {
return false;
}
return saved.equals(token);
}
删除同步令牌。
public synchronized void resetToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);
}
所谓知其然,还要知其所以然,这样才能来去自如嘛,嘿嘿!~