Ajax编程的核心就是能够从浏览器向服务器建立异步调用。通过建立这种会话,无需因每一次请求或用户交互而要求浏览重新加载。它可以在后台交换数据,浏览器以页面进行浙进式更新。利用客户端中心的开发模型,可以将逻辑从服务器转移至服务器,从而对应用程序有更多的控制。以这种方式,意味着服务器主要用于提供数据,而不提供应用逻辑。
一般来说,可以在服务器配置相应的WEB服务,而客户端利用Javascript异步调用服务器端的WEB服务。调用的过程都是可以通过异步的方式获取。
1.建立WEB服务
建立WEB服务可以参考《OReilly.Programming.ASP.NET.3rd.Edition》中的第15章,一个简单的WEB服务示例如下所示:
Stock.WebService
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services; //引入脚本服务的命名空间
// 建立Stock类
public class Stock
{
public Stock(){}
public Stock(string stockSymbol,String stockName,double stockPrice)
{
_stockSymbol = stockSymbol;
_stockName = stockName;
_stockPrice = stockPrice;
}
private string _stockSymbol;
private string _stockName;
private double _stockPrice;
public string StockSymbol{
get { return _stockSymbol; }
set { _stockSymbol = value; }
}
public string StockName {
get { return _stockName; }
set { _stockName = value; }
}
public double stockPrice {
get { return _stockPrice; }
set { _stockPrice = value; }
}
}
[WebService(Name="StockTickersWebServer",Namespace = "http://Dawnsoft.com/",Description="通过股票代码获得股票的相应信息")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
[System.Web.Script.Services.ScriptService] // 支持AJAX服务
public class StockTickerSimple : System.Web.Services.WebService
{
protected string[,] stocks =
{
{"MSFT","Microsoft","25.30"},
{"DELL","Dell Computers","34.83"},
{"HPQ","Hewlett Packard","20.47"},
{"YHOO","Yahoo!","34.50"},
{"GE","General Electric","36.20"},
{"IBM","International Business Machine","76.38"},
{"GM","General Motors","26.68"},
{"F","Ford Motor Company","9.11"}
};
public StockTickerSimple()
{
//如果使用设计的组件,请取消注释以下行
//InitializeComponent();
}
[WebMethod(CacheDuration=1800,Description="返回股票的价格")]
public double GetPrice(string StockSymbol) // 向外界暴露GetPrice方法
{
for(int i=0;i<stocks.GetLength(0);i++)
{
if(string.Compare(StockSymbol,stocks[i,0],true)==0)
return Convert.ToDouble(stocks[i,2]);
}
return 0;
}
[WebMethod]
public string GetName(string StockSymbol) // 向外界暴露GetName方法
{
for (int i = 0; i < stocks.GetLength(0); i++)
{
if(string.Compare(StockSymbol,stocks[i,0],true)==0)
return stocks[i,1];
}
return "Symbol not found";
}
[WebMethod]
public Stock GetStockObject(string stockSymbol) // 向外界暴露获得Stock对象的方法
{
for (int i = 0; i < stocks.GetLength(0); i++)
{
if (string.Compare(stockSymbol, stocks[i, 0], true) == 0)
{
Stock stock = new Stock(stockSymbol, stocks[i, 1], Convert.ToDouble(stocks[i, 2]));
return stock;
}
}
return null;
}
} 以上,一个WEB服务就建立了,你可以通过相应的URL查看WEB服务,如:http://localhost/DawnEnterprise/webServices/StockTickerSimple.asmx
并且通过http://localhost/DawnEnterprise/webServices/StockTickerSimple.asmx/js 链接可以看到一组Javascript函数,利用这些函数可以从脚本调用WEB方法,这些Javascript代码被称为代理(proxy)或称为WEB服务代理。
下面就是在页面上增加一个与WEB服务交互的页面
2. Javascript中调用WEB服务方法和页面上的方法
在Javascript中调用WEB服务方法有以下几点需要注意。
2.1 ScriptManager的WEB服务支持
第一步肯定是增加Ajax支持,增加Ajax支持的方法是加入ScriptManager控件。第二步是在ScriptManager控件中添加一个服务引用,如下所示:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/webServices/StockTickerSimple.asmx" InlineScript="true" />
</Services>
</asp:ScriptManager>
以上,ScriptManager控件的Service属性包含一个ServiceReference对象集合。一个ServiceReference对象就是一个本地WEB服务的引用。
ServiceReference的InlineScript属性设置为true说明Web服务代理程序会随页面下载,false则将代理程序单独下载到浏览器。
相关的Javascript代理程序如下图所示:
2.2 调用WEB服务
第二步肯定是要调用WEB服务,需要告知页面,是在什么场合下调用WEB服务呢,发生PageLoad事件,发生Button的Click事件..都可以作为调用WEB服务的场合。
以下所示的就是在点击Button后调用相应的WEB服务.
Ajax.CallWebService
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/webServices/StockTickerSimple.asmx" InlineScript="true" />
</Services>
</asp:ScriptManager>
<script type="text/javascript" language="javascript">
var ticketName = "";
var ticketPrice = "";
function getTicketNameAndPrice() {
var ticketSymbol = $get("txtTicketSymbol").value;
StockTickerSimple.GetName(ticketSymbol, onGetNameSuccess, onGetNameFailure, "<%=DateTime.Now%>");
StockTickerSimple.GetPrice(ticketSymbol, onGetPriceSuccess, onGetPriceFailure, "<%=DateTime.Now%>");
// 以上,第一个参数表示的是WebSerivce方法的参数,第二个参数表示的是成功返回时调用的函数,
// 第三个参数(可选)表示的是失败返回时调用的函数,第四个参数(可选)表示的是传入用户的上下文。
// 在两个回调函数中,都可以使用这个上下文,上下文可以是任何Javscript类型。
}
/* 以上四个都是回调方法 */
function onGetNameSuccess(result, context, methodName) {
ticketName = "Name:" + result;
if (ticketPrice != "")
$get("TickNameAndPrice").innerHTML = ticketName + " " + ticketPrice;
}
function onGetNameFailure(result, context, methodName) {
var errorMessage = error.get_message();
$get("TickNameAndPrice").innerHTML = errorMessage;
}
function onGetPriceSuccess(result, context, methodName) {
ticketPrice = "Price:" + result;
if(ticketName!="")
$get("TickNameAndPrice").innerHTML = ticketName + " " + ticketPrice;
}
function onGetPriceFailure(result, context, methodName) {
var errorMessage = error.get_message();
$get("TickNameAndPrice").innerHTML = errorMessage;
}
</script>
<input id="txtTicketSymbol" type="text" />
<input id="Button1" type="button" value="button" onclick="getTicketNameAndPrice()" /></div>
<div id="TickNameAndPrice"></div>
</form>
以上,从Javascript调用WEB服务时,成功或失败回在相应的回调函数中反映出来,但是,在异步的请况下,有时必须考虑处理一个请求所花费的时间。在某些情况下,可能希望调用立即返回,所以需要设置一个较短的时间值,在另外的情况下,可能需要设置较长时间,从而保证服务器有足够的时间来处理请求。所以,ASP.NET中客户端代理公布了一个timeout属性,可以利用这个属性调用超时间隔。
StockTickerSimple.set_timeout(1000); // 设置超时为1秒
如果超过时限,但还未收到响应,就会在客户端产生一个异常,并调用失败回调函数。
2.3 处理复杂类型
在前一节中,我们调用相关的WEB服务中的共开方法,这些方法返回的一个是字符串格式,一个是数值格式,但是,如果方法返回的是一个对象怎么办?在JS中怎么处理这个对象。
在我们编写的WEB服务中,有一个GetStockObject方法,它返回的是一个Stock类型,下面就是在JS中怎么利用这些对象的代码:
Ajax.CallWebService.ComplexObject
<script type="text/javascript" language="javascript">
function getTicketObject() {
var ticketSymbol = $get("txtTicketSymbol").value;
StockTickerSimple.GetStockObject(ticketSymbol, onGetObjectSuccess, onGetObjectFailure);
}
function onGetObjectSuccess(result, context, methodName) {
var ticketObject = result;
if (ticketObject)
$get("TickNameAndPrice").innerHTML = ticketObject.StockName + " " + ticketObject.stockPrice;
else
$get("TickNameAndPrice").innerHTML = "There is no result in the Tickets";
}
function onGetObjectFailure(result, context, methodName) {
var errorMessage = error.get_message();
$get("TickNameAndPrice").innerHTML = errorMessage;
}
<input id="txtTicketSymbol" type="text" />
<input id="Button2" title="" type="button" value="GetTickObject" onclick="getTicketObject()" /></div>
<div id="TickNameAndPrice"></div> 以下的例子是在JS中处理返回数组类型的代码:
Ajax.CallWebService.MutliObject
<div>
<input id="GetDeals" type="button" value="Get Deals" onclick="getDeals()" />
<div id="Deals"></div>
</div>
function getDeals(){
AspNetAjaxInAction.StarbucksService.GetDeals(onGetDealsSuccess,
onGetDealsFailure);
}
function onGetDealsSuccess(result, context, methodName){
// result由WEB服务的方法返回一个List<Beverage>对象,在JS中直接当作数组处理
var sb = new Sys.StringBuilder();
for (var i = 0; i < result.length; i++){
var bev = result[i];
sb.append(bev.Name + " – ");
sb.append(bev.Description + " – ");
sb.append(bev.Cost + "<br />");
}
2.4 在客户端创建服务器类型
在上一节,可以知道在客户端可以处理服务器中创建的类型,但是,有没有一种办法,可以在客户端实例化服务器端类的实例?
其中,如果类在WEB服务的文件中定义,完全可以直接定义相关的类,如以上的Stock类,可以在客户端这样定义:
var stockObject = new AspNetAjaxInAction.Stock();
stockObject.StockSymbol = "HAI";
stockObject.StockName ="Haier";
stockObject.StockPrice = 21.12;
定义好后,就可以作为对象传递给WEB服务的某个方法,在WEB服务中,完全有能力识别这个对象。
另外,如果定义的类不在WEB服务文件中呢,比如,定义在一个cs类文件中,这时候怎么使用这个类来实例化对象?这时候可以在WEB服务的文件(asmx)中告诉WEB服务把这个类包含在代理中。可以使用GenerateScriptType标签,并提供想要包含的类类型,这样子的话,这个类型也将在WEB服务代理中得到支持,如下所示:
WebService.GenerateScriptType
[ScriptService]
[GenerateScriptType(typeof(Employee))] // 引入外部类定义
[WebService(Namespace = "http://aspnetajaxinaction.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class StarbucksService : System.Web.Services.WebService
{
// WebService Class Define
}
相应的,在客户端可以直接定义和使用Employee类型:
function createEmployee(){
var emp1 = new AspNetAjaxInAction.Employee();
emp1.First = "Frank";
emp1.Last = "Rizzo";
emp1.Title = "Principal";
}
2.5 使用HTTP GET
在以上的异步调用中,对WEB服务所做的调用使用的都是HTTP POST方式,ASP.NET默认的从浏览器只接受这种类型的请求,要处理一个HTTP GET请求,必须显式的用ScriptMethod属性装饰一个方法,并设置UseHttpGet为True,那么调用这个方法的时候,将会使用HTTP GET方式
[ScriptMethod(UseHttpGet=true)] // 可以使用HTTP GET方式
[WebMethod]
public List<Beverage> GetDeals()
{
// WebService Method Define
}
2.6 调用页面方法
在以上所有的AJAX异步调用中,调用的方法都是WEB服务中的方法。AJAX还支持Javascript调用ASP.NET页面本身声明的方法。 这些方法在页上声明(即在aspx.cs)中声明,而不是在WEB服务中声明,所以被称为页面方法。调用页面方法的代码如下:
aspx.WebMethod.Prototype
[WebMethod] // 页面的方法中必须要有WebMethod特性修饰
public static string HelloEmployee(AspNetAjaxInAction.Employee emp) // 一般来说,也都是声明静态方法
{
return string.Format("Hello {0} {1}.", emp.First, emp.Last);
} 在页面Javascript代码中,ScriptManager的EnablePageMethods属性设置为true,就可以支持页面方法了。
Ajax.ScriptManager.EnablePageMethod
<asp:ScriptManager ID="ScriptManager1" runat="server"
EnablePageMethods="True">
<Services>
<asp:ServiceReference Path="StarbucksService.asmx" InlineScript="true" />
</Services>
</asp:ScriptManager>