Ajax聊天室.docx
《Ajax聊天室.docx》由会员分享,可在线阅读,更多相关《Ajax聊天室.docx(20页珍藏版)》请在冰豆网上搜索。
Ajax聊天室
Ajax聊天室
针对JSP聊天室存在的问题,Ajax聊天室做出了相应的改进。
正如前面提到的:
Ajax并不是取代B/S结构的应用,而是更好地完善了传统的Web应用。
针对JSP存在的两个问题,Ajax都有非常好的解决方案:
Ajax使用XMLHttpRequest异步发送请求,Ajax的服务器响应的仅是必需的数据,而不再是整个页面,必需的数据通过JavaScript在视图中显示。
使用Ajax可提高页面的复用:
浏览器从服务器下载一个页面后,不是一旦提交就丢弃该页面,立即进入下个页面——这种代价相当大,用户需要频繁下载完整页面;使用Ajax,则可以长时间地使用同一个页面,客户端可以很好地复用一个已下载的页面。
2.3.1异步发送请求
异步发送请求是Ajax最核心的内容,Ajax中的第一个字母就是Asynchronous(异步)的缩写,这也正说明了Ajax的核心。
Ajax使用XMLHttpRequest对象异步发送请求。
在某种程度上,XMLHttpRequest对象就是Ajax的核心,也是Ajax技术中唯一的新概念。
Ajax正是XMLHttpRequest这个新对象结合JavaScript,DOM和CSS后组成的新技术。
与JavaScript相似的语言还有JScript和ECMAscript。
它们的核心语法相似,作用也相似,只是在适应的浏览器以及各自的特性上存在小小的区别。
XMLHttpRequest在不同浏览器中的实现也不相同,因而创建XMLHttpRequest对象的方法也存在区别。
关于XMLHttpRequest更详细的信息,请参看第9章。
为了使用XMLHttpRequest对象,必须先创建XMLHttpRequest对象。
创建该对象的代码如下:
//创建XMLHttpRequest对象
functioncreateXMLHttpRequest()
{
//对于Mozilla浏览器
if(window.XMLHttpRequest)
{
//直接使用XMLHttpRequest函数来创建XMLHttpRequest对象
XMLHttpReq=newXMLHttpRequest();
}
//对于IE浏览器
elseif(window.ActiveXObject)
{
try
{
//使用AcitveXObject函数创建浏览器
XMLHttpReq=newActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
//如果出现异常,再次尝试以如下方式创建XMLHttpRequest对象
try
{
XMLHttpReq=newActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
}
}
}
}
前面已经讲过,XMLHttpRequest在不同浏览器中的实现机制不同,因而在不同的浏览器中创建XMLHttpRequest对象的方式也不相同。
虽然上面的代码尽量兼顾不同浏览器的实现,但不排除有些浏览器不支持上面的创建方法。
一旦XMLHttpRequest对象创建成功,系统便可以使用XMLHttpRequest发送请求。
XMLHttpRequest请求与传统请求不同,传统上发送请求需要提交表单或者加载新的地址,而XMLHttpRequest发送请求则完全通过JavaScript代码完成,从而避免了页面的刷新——这也是异步发送请求的核心。
XMLHttpRequest对象包含send方法,用于发送请求。
在发送请求之前,应先与请求的URL取得连接,XMLHttpRequest通过open方法打开与请求URL的连接。
下面是使用XMLHttpRequest发送请求的JavaScript代码:
functionsendRequest()
{
//input是个全局变量,对应于聊天信息的输入文本框
//调用聊天信息输入文本框的value属性,获取文本框的内容
varchatMsg=input.value;
//完成XMLHttpRequest对象的初始化
createXMLHttpRequest();
//定义请求的URL变量
varurl="chat.do";
//通过open方法取得与服务器的连接
//本系统发送POST请求
XMLHttpReq.open("POST",url,true);
//发送POST请求时,应该增加该文件头
XMLHttpReq.setRequestHeader("Content-Type","application/x-www-form-
urlencoded");
//指定XMLHttpRequest状态改变时的处理函数
XMLHttpReq.onreadystatechange=processResponse;
//发送请求后,将聊天信息的输入文本框清空
input.value="";
//发送请求,send的参数包含许多key-value对
//即以“请求参数名=请求参数值”的形式发送请求参数
XMLHttpReq.send("chatMsg="+chatMsg);//发送请求
}
上面的代码使用open方法打开与服务器URL的连接。
因为本系统采用POST发送请求参数,因此在请求里增加了Content-Type请求头,并将该请求头的值设为“application/x-www-form-urlencoded”,这是为了保证对请求参数采用合适的格式进行发送。
下面是使用XMLHttpRequest发送请求的步骤:
使用open方法连接服务器URL。
调用setRequestHeader方法为请求设置合适的请求头。
根据不同的请求,可能需要设置不同的请求头。
使用回调函数。
所谓回调函数,就是用于检测XMLHttpRequest状态的函数,当XMLHttpRequest状态发生改变时,该函数将自动执行。
执行send方法发送请求。
2.3.2解决多余刷新的问题
多余刷新在本聊天室的副作用还不是十分明显,因为本系统的界面修饰相当简陋,没有多余的图片等页面资源。
即使对于如此简陋的界面,一样可以对比两种模式下数据的流量。
对于上面的JSP聊天室,控制器处理用户请求后,转发到另一个JSP页面来显示处理结果;而对于Ajax的应用,控制器可以不再转发请求,因为仅需要生成较少数据的响应,控制器可以自己生成响应数据,此时服务器生成的不再是页面内容,而仅是必需的数据。
Ajax主要用于改善用户体验,是一种表现层技术,并不会影响到底层的技术。
对于J2EE应用而言,使用Ajax并不需要对中间层的任何组件做任何修改,更不需要对底层的DAO对象、DomainObject进行修改。
使用A
jax和使用Hibernate,IBAITIS或者Spring等框架没有任何冲突,结合Ajax技术后的J2EE应用将更加完美,带给用户更好的体验。
Ajax也可以与Struts,WebWork和JSF等框架结合使用。
事实上,Struts和JSF将在未来的版本中提供对Ajax更好的支持。
对于本系统而言,系统的业务逻辑组件ChatService没有任何改变,此处不再赘述。
控制器ChatServlet则有了简单的改变:
对于Ajax系统而言,服务器响应的不再是整个页面内容,而仅是必需的数据,ChatServlet不能将请求转发到chat.jsp页面。
此处,ChatServlet有两个选择:
直接生成简单的响应数据。
转向一个简单的JSP页面,使用JSP页面生成简单的响应。
本节将给出两种实现方式,用户可根据自己的需求进行选择。
2.3.2.1直接使用控制器生成响应数据
在这种模式下,Servlet直接通过response获取页面输出流,通过输出流生成字符响应。
在这种方式下,无须转发请求,系统处理更加简单:
//聊天使用的Serlvet,继承HttpServlet
publicclassChatServletextendsHttpServlet
{
//Servlet所使用的服务响应方法
publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException,ServletException
{
//设置编码方式,XMLHttpRequest对象总采用UTF-8方式发送请求
request.setCharacterEncoding("UTF-8");
//获取请求参数:
聊天信息
Stringmsg=request.getParameter("chatMsg");
//如果聊天信息不为空
if(msg!
=null&&!
msg.equals(""))
{
//通过session获取当前的聊天用户
Stringuser=(String)request.getSession(true).getAttribute("user");
//将聊天信息添加到系统当前的聊天记录中
ChatService.instance().addMsg(user,msg);
}
//设置中文输出流
response.setContentType("text/html;charset=GBK");
//获取页面输出流
PrintWriterout=response.getWriter();
//将当前系统的聊天记录输出到页面
out.println(ChatService.instance().getMsg());
}
}
该Servlet是一个非常简单的Servlet,获取请求参数,调用ChatService对象的业务方法,输出所有的聊天记录,但请注意该Servlet与完全生成视图的Servlet的区别:
该Servlet没有生成任何HTML标签,没有生成任何页面效果。
在这种情况下,也可以使用Servlet来生成客户响应。
上面的代码有两个值得注意的地方:
Ajax使用XMLHttpRequest发送请求,XMLHttpRequest发送请求时,所有参数都以UTF-8编码方式发送,因此request的setCharacterEncoding方法设置解码方式。
通过设置UTF-8的解码方式,才可以正确获取所有的请求参数。
生成响应时,一定要使用response的setContentType方法设置页面内容和编码方式。
尤其值得注意的是:
不能仅使用response.setHeader("Charset","GBK")语句。
仅使用该语句,系统采用GBK编码,但并没有确保页面是普通的HTML页面。
因为本书是在简体中文Windows环境下开发本系统的,故采用GBK编码方式。
对于上面的控制器而言,虽然生成了表现层内容,但并未完整地生成JSP页面,而是返回了模型数据,因而无须使用额外的JSP页面。
因为该响应数据是普通文本数据,而且相当简单,因而可以直接使用控制器生成客户端响应。
但如果需要生成的响应非常复杂,即响应生成的内容量大,而且具有丰富的表现格式,则应该考虑将请求转发到JSP页面,让JSP页面负责生成响应。
对于是否需要由JSP生成响应,不可一概而论,而应取决于响应的数据量以及表现格式。
2.3.2.2控制器转发到简单JSP页面生成响应
对于当前范例,这种做法是多此一举,控制器将请求转发到另外的JSP页面,而JSP页面仅负责输出聊天信息,下面是这种用法下的控制器代码:
publicclassChatServletextendsHttpServlet
{
publicvoidservice(HttpServletRequestrequest,HttpServletResponse
response)throwsIOException,ServletException
{
//设置解码格式
request.setCharacterEncoding("UTF-8");
//读取用户发送的聊天信息
Stringmsg=request.getParameter("chatMsg");
//如果发送的信息不为空
if(msg!
=null&&!
msg.equals(""))
{
Stringuser=(String)request.getSession(true).getAttribute("user");
ChatService.instance().addMsg(user,msg);
}
//将聊天记录设置成request属性
request.setAttribute("chatList",ChatService.instance().getMsg());
//转发请求
forward("/chatreply.jsp",request,response);
}
//用于控制forward请求的私有函数
privatevoidforward(Stringurl,HttpServletRequestrequest,
HttpServletResponseresponse)throwsServletException,IOException
{
ServletContextsc=getServletConfig().getServletContext();
RequestDispatcherrd=sc.getRequestDispatcher(url);
rd.forward(request,response);
}
}
控制器将聊天信息设置成request属性,然后在JSP页面中输出该聊天信息。
JSP代码如下:
<%@pagecontentType="text/html;charset=GBK"errorPage="error.jsp"%>
//输出当前的聊天信息
${requestScope.chatList}
这个JSP页面的作用也相当有限,仅完成简单的输出,因此使用JSP页面并不是十分必要。
2.3.3解析服务器响应
服务器响应生成简单的文本,而XMLHttpRequest包含一个属性:
responseText,该属性对应服务器响应生成的文本。
在解析服务器响应之前,必须先判断服务器响应是否完成以及响应是否正确,例如,生成404等错误响应是没有意义的。
为了判断服务器响应是否完成,响应是否正确,XMLHttpRequest同样提供了两个属性。
readyState:
判断服务器响应的状态,其中4表明响应完成。
status:
判断服务器响应对应的状态码,其中200表明响应正常,而404表明资源丢失,500表明内部错误等。
关于XMLHttpRequest的详细介绍可以参考第9章。
判断完响应状态后,可以使用responseText方法获取服务器响应文本,并将该文本输出到页面显示。
下面是解析、处理服务器响应的代码:
//用于处理服务器响应的程序
functionprocessResponse()
{
//如果服务器响应已经完成
if(XMLHttpReq.readyState==4)
{
//判断对象状态,如果服务器生成了正常响应
if(XMLHttpReq.status==200)
{
//信息已经成功返回,开始处理信息
//将聊天文本域的内容设置成聊天信息
document.getElementById("chatArea").value=XMLHttpReq.responseText;
}
else
{
//页面不正常
window.alert("您所请求的页面有异常。
");
}
}
}
此时,浏览器的页面通过JavaScript与服务器的通信基本完成。
客户端通过sendRequest函数向服务器发送请求,服务器通过ChatServlet处理用户请求,处理完用户请求后,有两种做法:
Servlet直接生成响应,或者将请求转发到JSP页面生成响应。
客户端通过processResponse处理服务器响应。
2.3.4何时发送请求
虽然定义了发送请求的方法,但没有定义何时发送请求。
根据聊天室的特点,请求应该是需要定时发送的,因为即使本人没有参与聊天,他也希望看到其他人的聊天记录,但该请求与前面介绍的请求存在少许差别,因为这种定时发送的请求无须读取聊天记录,无须发送聊天信息。
下面是这种定时发送请求的代码:
//定义定时发送的请求
functionsendEmptyRequest()
{
//创建XMLHttpRequest对象
createXMLHttpRequest();
//定义服务器响应的URL
varurl="chat.do";
//建立与服务器的连接
XMLHttpReq.open("POST",url,true);
//设置发送请求的参数格式
XMLHttpReq.setRequestHeader("Content-Type","application/x-www-form-
urlencoded");
//指定响应处理函数
XMLHttpReq.onreadystatechange=processResponse;
//发送请求
XMLHttpReq.send(null);
setTimeout("sendEmptyRequest()",800);
}
注意,sendEmptyRequest函数在最后调用了setTimeout("sendEmptyRequest()",800),setTimeout是JavaScript的计时器,该代码表示系统将在0.8s后再次执行sendEmptyRequest函数。
因此,该函数一旦开始执行就不会停止:
因为每次函数执行结束后,都将在0.8s后再次调用该函数。
自动发送的请求应在进入聊天室后立即发送,因此将该函数定义在页面加载时触发,也就是指定在bodyload时触发即可。
除此之外,还需要获取用户聊天信息,需发送带参数的请求。
这种请求应该定义在单击“提交”按钮或在聊天文本框中按下回车键时发送。
要实现按下回车键后发送请求很简单:
只需要为该键定义onclick事件即可。
如需在文本框中按下回车键时发送请求,则应为聊天文本框指定键盘处理函数,该函数监控文本框的所有键盘事件。
键盘处理函数的代码如下:
//键盘处理函数
functionenterHandler(event)
{
//定义键盘中发出事件的键
varkeyCode=event.keyCode?
event.keyCode:
event.which?
event.which:
event.charCode;
//回车键的代码为13,如果按下了回车键
if(keyCode==13)
{
sendRequest();
}
}
整个聊天HTML页面的代码如下:
DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN"
"http:
//www.w3.org/TR/html4/loose.dtd">
聊天页面--body的load事件中发送定时发送的请求-->
聊天页面 --下面定义聊天使用的文本域,该文本域用于显示当前聊天信息-->
--下面是输入聊天信息所使用的文本,并为onKeyPress事件指定了监听函数-->
--下面是输入聊天信息的文本框,并为onclick事件指定了监听函数-->
|
//将输入文本框定义成input变量
varinput=document.getElementById("chatMsg");
//将焦点定位在聊天输入框内
input.focus();
//系统使用的XMLHttpRequest对象
varXMLHttpReq;
//创建XMLHttpRequest对象
functioncreateXMLHttpRequest()
{
//对于Mozilla浏览器
if(window.XMLHttpRequest)
{
//直接使用XMLHttpRequest函数来创建XMLHttpRequest对象
XMLHttpReq=newXMLHttpRequest();
}
//对于IE浏览器
elseif(window.ActiveXObject)
{
try
{
//使用AcitveXObject函数创建浏览器
XMLHttpReq=newActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
//如果出现异常,再次尝试以如下方式创建XMLHttpRequest对象
try
{
XMLHttpReq=newActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
}
}
}
}
functionsendRequest()
{
//input是个全局变量,对应聊天信息的输入文本框
//调用聊天信息输入文本框的value属性获取文本框的内容
varchatMsg=input.value;
//完成XMLHttpRequest对象的初始化
createXMLHttpRequest();
//定义请求的URL变量
varurl="chat.do";
//通过open方法取得与服务器的连