Struts 2 的 Ajax 支持(二)

 

四、  基于 Dojo 的 pub-sub 事件模型


  pub-sub publish-subscribe 的缩写,意思是 发布-订阅, 它提供了一种事件处理函数的注册方式。实际上, Struts 2 pub-sub 事件模型基本是采用了 Dojo pub-sub 事件模型

 

4.1  pub-sub 的原理

    pub-sub 事件模型提供了一种简化的事件监听方式,通过 pub-sub 事件模型,可以让一个 JavaScript 事件同时触发多个事件处理函数。当我们把一个事件(也可能是一个普通函数) 作为一个发布者注册到一个主题后,如果该事件被触发(普通函数被调用),则该主题下所有的事件处理函数都会被自动调用。如下图:

Struts 2 的 Ajax 支持(二)_第1张图片

                                                   图 pub-sub 事件模型示意图

 

 

    Struts 2 并未对 Dojo pub-sub 事件模型进行过多的包装,如果有 Dojo 事件模型的基础,就可以非常容易理解 Struts 2 pub-sub 事件模型。

 

Dojo 中将一个事件注册到某个主题下的代码如下:

//将 foo 对象的 bar 方法注册到 /refresh 主题
dojo.event.topic.publish("/refresh","foo","bar");

提示: 事件主题是一个任意字符串,并没有太多额外的要求。

 

将某个事件处理函数注册到某个主题下的代码如下:

// 在 /refresh 主题下增加了一个匿名事件处理函数
dojo.event.topic.subscribe("/refresh", function(param1,param2){
    //this function will be called everytime "/refresh" is published
});
 

当然,Struts 2 在  Dojo pub-sub 事件模型基础之上,进行了简单的包装,它为大部分 Ajax 标签提供了如下 2 个属性:

 listenTopics : 指定系列事件主题名,多个主题之间以英文逗号(,)隔开。配置该元素的 HTML 元素将用于加载服务器响应。

 notifyTopics : 指定系列事件主题名, 多个主题之间以英文逗号(,)隔开。配置该属性的 HTML 元素将会把事件发布到指定主题,发布主题时会传递 3 个参数: data、type、request

 

因为 Struts 2 标签发布的事件总是会发送 3 个参数: data 、type 、request,这3个参数就代表了与服务器交互的内容

3 个参数的含义如下:

 data : 从服务器返回的数据

 type : 与服务器交互的状态,只有 3 个值: before (交互之前)、 load (加载数据中) 、 error (服务器响应出错)

 request : 代表请求对象本身

 

因此, Struts 2 中的事件处理函数大致上都是如下形式:

//事件处理函数包含 3 个参数
function a(data,type,request){

}
 

4.2  pub-sub 的示例

   下面代码中使用了一个按钮来发布事件,将事件发布到指定主题;并通过 JavaScript 代码来为该主题增加一个事件处理函数。

 

/pub-sub/pub-sub1.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>pub-sub模型</title>
	<sx:head/>
	<script type="text/javascript">
	//订阅/lee主题,使用匿名函数监听
	dojo.event.topic.subscribe("/lee", function(data, type, request)
	{
		alert('正处于Dojo的异步交互过程中,类型是:' + type);
		alert(request);
	});
	</script>
</head>
<body>
<h3>简单pub-sub模型</h3>
<!-- 单击该按钮时将发布事件到/lee主题 -->
<sx:submit type="submit" value="提交" align="left" notifyTopics="/lee"/>
</body>
</html>

  上面代码中,定义 “提交“ 按钮时 ,指定了 notifyTopics="/lee" 属性,该属性指定单击按钮时,单击事件将被发布到 /lee 主题

 

  在上面 JavaScript 代码中,使用 dojo.event.topic.subscribe("/lee", function(data, type, request){}); 代码为 /lee 主题注册了一个匿名的事件处理函数。

 

  上面事例代码是直接指定了事件处理函数作为 /lee 主题的订阅者。除此之外,我们也可以指定一个或多个 Struts 2 标签元素作为主题的订阅者,定义订阅者通过为 Struts 2 标签指定 listenTopics 属性实现。一旦指定了一个或多个 Struts 2 标签作为订阅者,这样每当有事件被发布到该主题时,这些 Struts 2 标签将被 “激发” 一次

 

/pub-sub/pub-sub2.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>pub-sub模型</title>
	<sx:head/>
</head>
<body>
	<h3>pub-sub模型</h3>
	<!-- 定义发布事件的提交按钮 -->
	<sx:submit type="submit" value="更新" notifyTopics="/change"/>
	<!-- 定义2个订阅事件的Struts 2标签 -->
	<sx:div id="t1" cssStyle="background-color:#bbbbbb;width:360px;	height:80px" 
		listenTopics="/change" href="../ajax/book.action"/>
	<sx:div id="t2" cssStyle="border:2px solid black;width:360px; height:120px" 
		listenTopics="/change" href="../ajax/price.action" />
</body>
</html>

   上面代码让一个 更新 ” 按钮作为事件发布者让两个 <sx:div /> 元素作为事件的订阅者 ,意思就是每当单击 “更新 ” 按钮时,该按钮将会把单击事件发布到 /change 主题,而订阅者 <sx:div /> 元素的默认行为(更新自己的内容)被触发 。关于 <sx:div /> 标签的用法,请看下一节。

 

  使用 <sx:div /> 元素时,指定了一个 href 属性,该属性指向一个 Action , 该 Action 负责生成该元素里的内容。上面 2 个 book.actionprice.action 都直接使用 ActionSupport 作为处理类。

 

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
	<constant name="struts1custom.i18n.resources" value="messageResource"/>
	<constant name="struts.i18n.encoding" value="GBK"/>
	
	<package name="ajax" extends="struts-default" namespace="/ajax">
		<action name="*">
			<result>/pub-sub/{1}.jsp</result>
		</action>
		<action name="">
			<result>.</result>
		</action>
	</package>	
</struts>    

 

/pub-sub/book.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%=Math.random() > 0.5 ? "疯狂Java讲义" : "Struts 2权威指南"%>
 

/pub-sub/price.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<h3>今日报价</h3>
<ul>
	<li>西红柿:<%=java.lang.Math.random() * 10%>元/斤</li>
	<li>黄瓜:<%=java.lang.Math.random() * 10%>元/斤</li>
	<li>芹菜:<%=java.lang.Math.random() * 10%>元/斤</li>
</ul>

 通过上面可以看出,每次单击“更新 ”按钮时,将会引起页面中 2 个 DIV 元素里内容的改变。

 从上面的介绍可以看出,Struts 2 的 pub-sub 事件模型基本上保留了 Dojo 事件模型特征,而且在原有的基础上,提供了进一步简化。通过使用 Struts 2 的事件模型,可以以更简单的方式来动态更新页面的内容。

<script type="text/javascript">
      function newWin(url){
         window.open(url, "newwindow", "width=800,height=500,top="+(screen.availHeight-500)/2+",left="+(screen.availWidth-600)/2+", toolbar=no, menubar=no, scrollbars=yes, resizable=no, location=yes, status=no");
      }
</script>

<s:a href="" onclick="newWin('pub-sub/pub-sub1.jsp');" cssStyle="cursor: hand;">pub-sub1.jsp</s:a>
   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<s:a href="" onclick="newWin('pub-sub/pub-sub2.jsp');" cssStyle="cursor: hand;">pub-sub2.jsp</s:a>
 

4.3  阻止请求

如果想阻止某个 Ajax 过程,则可以先将该 Ajax 标签的事件注册到某个事件主题下,再为该主题增加事件订阅者,并在该事件订阅者中取消该请求


例如,可以通过如下代码将 Ajax 按钮的 Ajax 事件注册到 /request 事件主题下:

<sx:submit type="submit" value="submit" notifyTopics="/request" href="ajaxText.action"/>

下面为 /request 事件主题增加订阅者,并在订阅者中取消 request 如下:

//为 /request 事件主题增加订阅者
dojo.event.topic.subscribe("/request",function(data,type,request){
   //取消 request 的 Ajax 请求
   request.cancel= true;
});
 

通过上面代码即可强行中止 Ajax 请求

 

五、  基于 Dojo 的 Ajax 标签

  Struts 2 的 Dojo 插件为了简化 Ajax 过程,提供了一些常用的 Ajax 标签,这些常用的 Ajax 标签基本可以满足普通的 Ajax 需要。但对于一些更复杂的 Ajax 通信过程,Struts 2 提供的 Ajax 标签则不够灵活,所以可以使用后面的 JSON 插件来实现

 

5.1  div 标签

  <sx:div /> 标签在页面上生成一个 HTML 的 <div/> 元素,但这个 <div/> 元素的内容不是静态内容,而是从服务器获取的内容。为了让 <sx:div/> 标签能取得服务器的数据,必须为 <sx:div /> 标签指定一个 href 属性,这个 href 属性值是一个 Action ,该 Action 负责生成该 <div/> 元素的内容

 

  还可指定该 <sx:div/> 标签生成的 <div/> 元素以固定的频率来更新自身的内容,为了指定更新频率和更新延迟,可以指定如下两个属性:

 updateFreq : 指定更新 Div 内容的时间间隔,单位是 ms 。如果不指定,则只在页面加载时更新该 Div 的内容

 delay : 指定更新 Div 内容的时间延迟,单位 ms , 如果不指定,且指定了 updateFreq 属性,则页面加载后立即开始计时,准备刷新该 Div 的内容。如果没有指定 updateFreq ,则该属性没有任何意义


如果服务器的响应包含了 JavaScript 代码,且希望在本页面内执行,则可以为该 div 标签指定 executeScripts = "true"

/dojo/sx-div1.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>远程div</title>
    <sx:head/>
</head>
<body>
仅一次获取服务器内容的div<br />
<sx:div id="div1"
        cssStyle="border: 1px solid black;background-color:#dddddd;
			width:300px;height:40px;padding-top:8px;padding-left:20px"
        href="random.action">
	初始化文本
</sx:div>
动态更新内容的div,每隔1s刷新一次(通过指定updateFreq="1000")<br />
使用indicator(通过指定indicator="indicator")<br />
<sx:div id="div2"
	cssStyle="border: 1px solid black;background-color:#dddddd;
	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="random.action"
	updateFreq="1000"
	indicator="indicator">
	初始化文本
</sx:div>
<img id="indicator" alt="Loading..." style="display:none"
	src="${pageContext.request.contextPath}/dojo/images/indicator.gif"/><br />
3s之后才开始更新(通过指定delay="3000")<br />
指定与服务器交互出错的提示(通过指定errorText属性)<br />
指定与服务器交互过程中的提示(通过指定loadText属性)<br />
<sx:div id="div3"
	cssStyle="border: 1px solid black;background-color:#dddddd;
	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="random.action"
	updateFreq="1000"
	delay="3000"
	errorText="加载服务器数据出错"
	loadingText="正在加载服务器内容">
	初始化文本
</sx:div>
指定显示系统出错提示(通过指定showErrorTransportText="true")<br>
<sx:div id="div4"
	cssStyle="border: 1px solid black;background-color:#dddddd;
	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="/dojo/AjaxNoUrl.jsp"
	updateFreq="1000"
	showErrorTransportText="true"
	loadingText="正在加载服务器内容">
	初始化文本
</sx:div>
执行服务器脚本(通过指定executeScripts="true")
<sx:div id="div5"
	cssStyle="border: 1px solid black;background-color:#dddddd;
	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="test.action"
	updateFreq="9000"
	executeScripts="true"
	loadingText="正在加载服务器内容">
	初始化文本
</sx:div>
</body>
</html>

   上面的页面代码中,分别定义了几种不同的 div ,有的没有 updateFreq 属性,则该 div 的内容只在页面加载时一次更新;有的指定了 updateFreq ,则指定该 div 内容的固定更新频率;有的指定了  executeScripts="true" ,则该 div 元素可以执行远程服务器端的 JavaScript 代码。


除此之外,上面的 Div 还使用了如下 2 个 Ajax 标签的通用属性:

 showErrorTransportText : 设置是否显示服务器的错误信息

 loadingText : 设置当服务器响应还未加载时的显示信息

 

/dojo/randomStr.jsp

<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
	request.setAttribute("decorator", "none");
	//阻止浏览器缓存页面内容
	response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
	response.setHeader("Pragma","no-cache"); //HTTP 1.0
	response.setDateHeader ("Expires", 0); 
%>
服务器返回的随机数字是:<s:property value="rdmStr"/>
 

 

/dojo/testjs.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%
	request.setAttribute("decorator", "none");
	//阻止浏览器缓存
	response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
	response.setHeader("Pragma","no-cache"); //HTTP 1.0
	response.setDateHeader ("Expires", 0); 
%>
<script language="JavaScript" type="text/javascript">
    alert('疯狂Java讲义');
</script>
轻量级Java EE企业应用实战
<script language="JavaScript" type="text/javascript">
    alert('疯狂Ajax讲义');
</script>

  上面包含了一些 JavaScript 代码,为了在目标页面中执行这些 JavaScript 代码,需要为 <sx:div > 标签指定 executeScripts="true" 属性

 

RandomAction

package js.dojo;

import com.opensymphony.xwork2.ActionSupport;

public class RandomAction extends ActionSupport {
    private String data;

    public String getRdmStr() {
        String result = Math.round(Math.random() * 10000) + "";
        // 返回data和随机字符串连缀而成的字符串
        return data != null && !data.equals("") ? data + result : result;
    }

    public void setData(String data) {
        this.data = HTMLDecoder.decode(data);
    }

    public String getData() {
        return this.data;
    }
}

 

HTMLDecoder

package js.dojo;

public class HTMLDecoder {
    public static String decode(String str) {
        // 获取字符串中所有数字
        String[] tmp = str.split(";&#|&#|;");
        StringBuffer sb = new StringBuffer("");
        // 处理每个tmp数组中每个字符串元素
        for (int i = 0; i < tmp.length; i++) {
            // 如果该元素是5位数字,将其转换成非西欧字符
            if (tmp[i].matches("\\d{5}")) {
                sb.append((char) Integer.parseInt(tmp[i]));
            }
            // 对于普通字符
            else {
                sb.append(tmp[i]);
            }
        }
        return sb.toString();
    }
}
 

Struts.xml

<struts>   	
	<constant name="struts.custom.i18n.resources"
		value="messageResource"/>
	<constant name="struts.i18n.encoding" value="GBK"/>	
	 <include file="struts_4.xml"/>
	<package name="js"  extends="struts-default" namespace="/09">

  
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.2.dtd">
<struts>
	<constant name="struts.custom.i18n.resources"
		value="messageResource"/>
	<constant name="struts.i18n.encoding" value="UTF-8"/>	
	<package name="js.dojo" extends="struts-default">
		<action name="random" class="js.dojo.RandomAction">
			<result>/dojo/randomStr.jsp</result>
		</action>
		<!-- 配置ajaxText Action -->
		<action name="ajaxTest" class="js.dojo.AjaxTestAction">
			<result>/dojo/ajaxResult.jsp</result>
		</action>
		<action name="test">
			<result>/dojo/testjs.jsp</result>
		</action>
		<action name="books">
			<result>/dojo/data_zh_CN</result>
		</action>
        <action name="getBooks" class="js.dojo.GetBooksAction">
            <result>/dojo/books.jsp</result>
        </action>
        <action name="showPerson" class="js.dojo.ShowPersonAction">
            <result>/dojo/showPerson.jsp</result>
        </action>
        <action name="getChild" class="js.dojo.GetChildNode">
            <result>/dojo/bookNode.jsp</result>
        </action>
		<action name="">
			<result>.</result>
		</action>
	</package>
</struts>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
	<display-name>struts2</display-name>
	<welcome-file-list> 
	  <welcome-file>index.jsp</welcome-file>
	</welcome-file-list>  
    <filter>  
        <filter-name>struts2</filter-name>  
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
    </filter>  
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>
 <s:a href="" onclick="newWin('dojo/sx-div1.jsp');" cssStyle="cursor: hand;">sx-div1.jsp</s:a>
  

如果不需要 div 调用远程 java 方法,而是定期执行某个 JavaScript ,则可以为该 div 标签指定一个 handler 属性,该属性的值为该 JavaScript 函数即可,如下:


/dojo/sx-div2.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>远程div</title>
    <sx:head/>
</head>
<script type="text/javascript">
function handler(widget, node) {
	alert('本地JavaScript函数处理动态div');
	node.innerHTML = Math.random() > 0.4 ?
		"疯狂Java讲义" : "Struts 2权威指南";
}
</script>
<body>
直接使用本页面的JS函数,不再调用远程服务器<br />
<sx:div  id="div1"
	cssStyle="border: 1px solid black;background-color:#dddddd;	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="random.action"	updateFreq="4000"	handler="handler">
	初始化文本
</sx:div>
</body>
</html>

上面代码中,为 <sx:div> 指定了一个 handler 属性,该属性指向一个 JavaScript 函数。这意味着 div 每次刷新,该 JavaScript 函数都会被调用。 上面即指定了 handler 又指定了 href ,但该 div 标签不会再调用 href 属性指定的 Action ,所以 href 属性可以去除

 

 

除此之外,<sx:div> 标签还可将一个表单里包含的所有表单域转换成请求参数,并且把这些请求参数发送给远程服务器 。为了让一个 <sx:div> 标签发送表单里包含的表单域,可以为该 <sx:div> 标签指定如下属性:

 formId : 该属性的属性值为一个表单元素的 ID , 表明该 <sx:div> 标签会把该表单里的所有表单域转换成请求参数发送到服务器

 formFilter : 指定一个函数用于过滤指定表单内的表单域

 

除此之外,为了通过在 JavaScript 代码中手动控制 <sx:div> 标签启动自动更新,关闭自动更新,则可以为该 div 标签指定如下 2 个属性:

●  startTimerListenTopics : 设置一个监听的事件主题,当有 Struts 2 组件向该主题发布事件时,该 div 标签的计时器被启动

 stopTimerListenTopics : 设置一个监听的事件主题,当有 Struts 2 组件向该主题发布事件时,该 div 标签的计时器被关闭

 

/dojo/sx-div3.jsp

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>远程div</title>
    <sx:head/>
</head>
<script type="text/javascript">
var controller = 
{
	refresh : function() {alert("手动刷新");},
	start : function() {alert("启动自动刷新");},
	stop : function() {alert("停止自动刷新");}
};
//将controller的refresh方法注册成/refresh主题的发布者
dojo.event.topic.registerPublisher("/refresh", controller, "refresh");

//将controller的start方法注册成/startTimer主题的发布者
dojo.event.topic.registerPublisher("/startTimer", controller, "start");

//将controller的stop方法注册成/stopTimer主题的发布者
dojo.event.topic.registerPublisher("/stopTimer"	, controller, "stop");

//为after主题指定一个事件订阅者
dojo.event.topic.subscribe("/after"	, function(data, type, e){
		alert('与服务器交互过程中. 现在的过程类型是:' + type);
	});
</script>
<body>
<form id="testForm">
	<s:textfield name="data" label="输入用户数据"/>
</form>
<input type="button" value="手动刷新" 	onclick="controller.refresh();" />
<input type="button" value="停止计时器"	onclick="controller.stop();" />
<input type="button" value="启动计时器" 	onclick="controller.start();" />
<br/>
使用pub-sub机制(通过指定listenTopics等属性)<br/>
发送表单请求参数(通过指定formId="form")<br/>
<sx:div id="div1"
	cssStyle="border: 1px solid black;background-color:#dddddd;	width:300px;height:40px;padding-top:8px;padding-left:20px"
	href="random.action"	loadingText="正在加载服务器内容..."
	listenTopics="/refresh"	startTimerListenTopics="/startTimer"	stopTimerListenTopics="/stopTimer"
	updateFreq="9000"	autoStart="true"	formId="testForm"	notifyTopics="/after">
	初始化文本
</sx:div>
</body>
</html>

 从上面代码中可以看出,单击“停止计时器 ” ,触发 controller stop 方法,该方法会发布一个事件到 /stopTimer 主题,而页面中的 <sx:div > 标签的 stopTimeListenTopics 属性正是 /stopTimer ,这将引起计时器停止计时,即该<sx:div> 标签的内容停止自动更新。


点击“手动刷新”或则 “启动计时器”,当在文本框中输入 java  字符串,将在下面的文本框内看到 java 字符串,即表单里的请求参数已经被发送到了服务器

 

 

 

 

 

 

 

 

你可能感兴趣的:(JavaScript,Ajax,jsp,struts,dojo)