Ajax最大的优越就是异步编程,而异步交互的基础是服务器和客户端间的交互。两者之间交互的底层原理不怎么明白,只知道他们之间需要遵循一些协议,这篇博客的重点是从代码的角度,结合实例,从应用层的角度,学习一下Ajax是怎么在服务器和客户端进行交互的?
实例说明:
这里还是用上篇博客,采用的实例:单击页面上的按钮,弹出相应的提示信息。
技术分析:
上一篇博客的重点是说明Ajax的面向对象的思想基础,实现的方式是在页面(客户端)中,采用javascript脚本,定义了Person和Employee两个类,并用代码展示了两个类之间的继承关系的实现。最后使用两个按钮,通过在alert方法中实例化Employee类,展示出相关的信息。
这里使用Ajax的服务器端和客户端的交互,来实现上面的例子。也就是说事先在服务器端封装好一些类,然后通过客户端来访问这些类
以下先对客户端和服务器端的交互做个概述。
服务器端:
1. 在App_Code文件夹下创建一个Employee类,封装一些与显示相关的信息和方法。
2. 添加允许Asp.Net 的Ajax框架从脚本中调用服务端 Web 服务的方法。
3.添加一般处理程序,自定义Http请求的方法 。
客户端:
1. 客户端使用javascript脚本,定义方法从服务器端获得web服务,通过回调函数,实现客户端和服务器端的交互。
通过上面的概述,让我们队客户端和服务端之间的交互有一个大概的了解。下面通过具体的代码实现,来说明客户端和服务端之间的交涉。
还是先从服务端做起:
1.创建App_Code文件夹下的Employee类。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace AspNetAjaxOverview
{
/// <summary>
/// Summary description for Employee
/// </summary>
public class Employee
{
private string _FirstName;
private string _LastName;
private string _Title;
public Employee() { }
public Employee(string firstName, string lastName, string title)
{
this._FirstName = firstName;
this._LastName = lastName;
this._Title = title;
}
public string FirstName
{
get
{
return this._FirstName;
}
}
public string LastName
{
get
{
return this._LastName;
}
}
public string Title
{
get
{
return this._Title;
}
}
}
}
我们可以看到这里的Employee类跟我们在学习分层时创建的实体类是一模一样的。在Employee类中封装了一些字段,并通过对外公开的属性来访问或修改内部字段。
程序运行的时候会把App_Code文件夹下面所有的文件编译成一个程序集,供程序调用,该文件夹主要存放一些页面通用的赋值类,里面的类和普通的类一样。
2.添加web服务
右击程序集名称,添加一个Web服务。
注意这里创建的是web服务不是一般的web页面,web服务的扩展名是asmx,它没有像aspx页面那样的界面,在这里我们通常定义一些为客户端服务的方法,包括支持Ajax框架从脚本中调用这里的方法,但是需要取消默认的[System.Web.Script.Services.ScriptService]注释,并在[WebMethod]下面添加[ScriptMethod]语法,表明是以下方法是可以供客户端javascript调用的方法。
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
//这个是通过创建web服务添加的
namespace AspNetAjaxOverview
{
//命名空间
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
//允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务(这里需要取消默认的注释)
[System.Web.Script.Services.ScriptService]
public class EmployeeService : System.Web.Services.WebService
{
[WebMethod]
//添加下面标记后可以在客户端使用该方法
[ScriptMethod]
//这里的方法是之前C#写的代码,与前台的Ajax语法无关,只是被调用而已
public Employee GetEmployee(string firstName, string lastName, string title)
{
return new Employee(firstName, lastName, title);
}
}
}
我们注意到在添加的命名为EmployeeService.asmx的服务端内,首先注明了它的命名空间,继承自System.Web.Services.WebService,通过[WebMethod]和[ScriptMethod]来说明自定义的GetEmployee方法是让客户端调用的方法。
3.添加一般处理程序,命名为GetEmployee.ashx(不是web页aspx哦) ,方法参考添加服务器应用程序的添加。
using System;
using System.Web;
using System.Web.Script.Serialization;
namespace AspNetAjaxOverview
{
/// 通过添加一般处理程序
/// HttpHandler是一个彻底自定义Http请求的方法,它通过web.config来定义Asp.Net运行时来过滤出要自定义的Http请求,发送到定义在web.config的指定类中
/// 他处理控件的回发事件非常麻烦,所以,一般使用.ashx,用来输出一些不需要回发处理的项目
//获取参数信息,使用json格式输出employee类的字串
public class GetEmployee : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//指明是文本类型,还可以是图片
context.Response.ContentType = "text/plain";
//取出需要信息
string firstName = context.Request.Params["firstName"];
string lastName = context.Request.Params["lastName"];
string title = context.Request.Params["title"];
//构造employee
Employee employee = new Employee(firstName, lastName, title);
//实例化jsonEmp字符串,便于在客户端使用的数据格式
JavaScriptSerializer serializer = new JavaScriptSerializer();
string jsonEmp = serializer.Serialize(employee);
//输出json字符串
context.Response.Write(jsonEmp);
}
//这里的方法不可少
public bool IsReusable
{
get
{
return false;
}
}
}
}
GetEmployee实现的是IHttpHandler接口,自定义Http请求的方法,它通过web.config来定义Asp.Net运行时来过滤出要自定义的Http请求,发送到定义在web.config的指定类中。一般处理程序处理控件的回发事件非常麻烦,所以,一般使用.ashx,用来输出一些不需要回发处理的项目。
这里的GetEmployee类,通过从服务器端获取参数信息,封装到实例化的employee类中,然后使用json格式输出employee类的字串。
客户端:
费了这么大的劲,终于完成了服务端的服务,接下来就是从客户端来调用上面的方法了。
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Asynchronous Communication Layer Overview</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<script language="javascript" type="text/javascript">
//ajax和服务器之间是通过request对象来传播数据库,传播方式有post和get等,
//服务器和客户端间的交互通过回调函数实现
//最终的结果通过前台的控件显示出来
function showEmployee(firstName, lastName, title) {
//构造request对象
var request = new Sys.Net.WebRequest();
request.set_url('GetEmployee.ashx');
request.set_httpVerb("POST");
//回调函数,通过回调函数实现服务器端呼叫客户端
request.add_completed(onGetEmployeeComplete);
var requestBody = String.format(
"firstName={0}&lastName={1}&title={2}",
encodeURIComponent(firstName),
encodeURIComponent(lastName),
encodeURIComponent(title));
request.set_body(requestBody);
//客户端对象向服务器端对象发送信息
request.invoke();
}
//客户端的回发事件
function onGetEmployeeComplete(response)
{
if (response.get_responseAvailable())
{
var employee = response.get_object();
alert(String.format(
"Hello I'm {0} {1}, my title is '{2}'",
employee.FirstName,
employee.LastName,
employee.Title));
}
}
</script>
<%--按钮调用客户端事件--%>
<input type="button" value="Bill Gates"
onclick="showEmployee('Bill', 'Gates', 'Chair man')" />
<input type="button" value="Steve Ballmer"
onclick="showEmployee('Steve', 'Ballmer', 'CEO')" />
</form>
</body>
</html>
我们可以看到这里的客户端的javascript定义的脚本和上一篇博客中已经大不相同了,这里我们定义了一个showEmployee的方法,在该方法中通过构造request对象,在该对象中封装需要向服务器端交互的数据,包括,url提交动作方式post等,而且定义了一个回调函数onGetEmployeeComplete,通过这个回调函数,实现服务器端呼叫客户端。客户端通过按钮来来触发showEmployee事件,然后调用onGetEmployeeComplete回调函数,最终实现了客户端与服务端的交互。
但是上面的方法是一种比较笨拙的实现,这种方法并没有充分利用在服务端已经定义好的GetEmployee一般处理程序,下面是一种更为简洁的实现:
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Web Service Access</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<%--注册WebService,指明使用的服务路径--%>
<asp:ServiceReference Path="EmployeeService.asmx" />
</Services>
</asp:ScriptManager>
<script language="javascript" type="text/javascript">
//相当于服务器端的代理,将回调函数传入即可
function showEmployee(firstName, lastName, title) {
//直接使用服务器端的方法
AspNetAjaxOverview.EmployeeService.GetEmployee(
firstName,
lastName,
title,
onGetEmployeeSuccess);
}
//回调函数,直接使用employee对象
function onGetEmployeeSuccess(employee)
{
alert(String.format(
"Hello I'm {0} {1}, my title is '{2}'",
employee.FirstName,
employee.LastName,
employee.Title));
}
</script>
<input type="button" value="Bill Gates"
onclick="showEmployee('Bill', 'Gates', 'Chair man')" />
<input type="button" value="Steve Ballmer"
onclick="showEmployee('Steve', 'Ballmer', 'CEO')" />
</form>
</body>
</html>
对比客户端的两种方法,我们可以看到第二种实现,在ScriptManager中注册了要使用了服务端,在showEmployee方法中使用服务器端的AspNetAjaxOverview.EmployeeService.GetEmployee方法来处理客户端的参数封装和交互方式,这种方法大大减少了客户端的代码,通过复用服务端的GetEmployee方法,处理客户端需要向服务端提交的参数和动作方式,大大简化了程序的开发。
两篇博客中实现方法对比:
在上一篇博客中,完全是靠客户端的javascript脚本来实现,这里把显示的方法挪到了服务器端,然后通过Ajax框架下的服务端和客户端间的交互,在客户端通过框架来调用服务端的方法来实现。
个人之见,不能绝对的判定哪个方法的好坏,要视实际情况而定,如果有大量的使用显示相关信息的方法采用第二种做法可以减少程序员的工作量,而且javascript脚本是一种解释性语言,不会自动编译,所以,在效率上要低一些,不过仅这里的实例来说,一点点的效率是无足轻重的。
总结:这篇博客重点采用了两种方法实现了Ajax框架下的客户端和服务器端的交互。对服务器、一般处理程序和客户端之见的相互作用模型有了一个初步的认识,对以后的大型的开发是一个必备的基础。
下一篇博客,总结一下Ajax框架下的两种自定义错误的方法,望路过朋友多关注,并多多指教、交流。