ScriptManager是ASP.NET Ajax技术中最关键的组件,每一个页面都必须包含一个ScriptManager。他包含的属性有:
1. AllowCustomErrorsRedirect—决定updatapanel局部刷新时出现错误是否跳转到自定义错误界面。
2. AsyncPostBackErrorMessage—设定当updatapanel刷新错误时显示的字符串。
3. AysncPostBackTimeout—设定updatapanel异步刷新的超时值。
4. AuthenticationService—设定客户端验证服务的设置。
5. EnablePageMethods—设置使页面可以使用基于页面的web服务。
6. EnablePartialRendering—通知Ajax 客户端运行库给updatapanel提供支持。True设置提供支持,false不提供支持。
7. EnableScriptGlobalization—设定script是否为全球化。
8. EnableScriptLocalization—设定script是否为本地化。
9. LoadScriptBeforeUI—设定是否在浏览器显示界面前载入脚本。
10. ProfileService—提供客户端个性化服务设置。
11. ScriptModel—设定载入的脚本类型(测试版或发行版)。
12. ScriptPath—设定非web resource程序集脚本的下载路径。
13. Script—页面中需要的脚本文件名集合。
14. Service—页面中需要调用的web服务集合。
关于HTTP 请求:调用web服务的Http请求分为两种方式:Post和Get。在Post方式是调用web服务的默认方式,在这种方式下,一个整体的数据将会从浏览器传输到服务器,这些数据的大小是不受限制的,所以这种方式比较合适大量数据传输的情况中。浏览器把数据格式化为JSON格式并传输到服务器,服务器会解析JSON数据并格式化为.NET 数据,然后启动调用web服务。在Get方式下,只有初始化方式与post不同,其他方式都是一样的。浏览器会将一定的参数作为查询字符串传输到服务器,数据传输的大小是有限制的,并会存在一个安全上的问题。要改成get模式要使用ScriptMethod属性:
[ScriptMethod(UseHttpGet=true)]
Javascript的简化形式:
Javascript在ASP.NET Ajax里面有了一些简化方式:
1.$get(ControlName)-$get是Sys.UI.DomElement.getElementById和DOM中document.getElementById的便捷写法。
2.$addhandler(Element,EventName,HandlerMethod)-是Sys.UI.DomEvent.addHandler.Elment的便捷写法。它将为一个form元素添加一些事件。EventName是事件的名称,比如“Click”等。
3.$clearHandlers(Element)-是Sys.UI.DomElement.clearhandlers.Element的便捷写法。
4.$create()-sys.Component.create的便捷写法。
5.$find()-Sys.Application.findComponent的便捷写法。
6.removeHandler()-$removeHandler是Sys.UI.DomEvent.removeHandler的便捷写法。
调用Web服务的例子:
(1)客户端代码:
<asp:ScriptManager ID="ScriptManager1" runat="server" >
<scripts>
<asp:ScriptReference Path="TestScriptFile.js"></asp:ScriptReference>
</scripts>
<services>
<asp:ServiceReference Path="WebServices/GetData.asmx"></asp:ServiceReference>
</services>
</asp:ScriptManager>
<script language="javascript" type="text/javascript">
function pageLoad()
{
GetCodeCampList();
}
function GetCodeCampList()
{
GetData.CodeCampList(CodeCampListOnCompleteCallBack,
CodeCampOnServerException);
}
function GetCodeCampInfo()
{
var iCodeCamp;
iCodeCamp = $get("CodeCamp").options[$get("CodeCamp").selectedIndex].value;
GetData.CodeCampInfo(iCodeCamp, CodeCampInfoOnCallBack, CodeCampOnServerException);
}
function CodeCampListOnCompleteCallBack(result)
{
var iLength = document.getElementById("CodeCamp").options.length;
for(i=0; i<iLength; i++)
{
$get("CodeCamp").options[0] = null;
}
document.getElementById("CodeCamp").options.add(new Option("", ""));
for(i=0;i<result.length;i++)
{
$get("CodeCamp").options.add(
new Option(result[i].Name,
result[i].CodeCampId));
}
$get("ExceptionInfo").style.visibility = "hidden";
}
function CodeCampOnServerException(result)
{
var ExceptionOutput;
var Return = "<br />";
if ( null != result )
{
for(m in result)
alert(m);
ExceptionOutput = "Message: " + result.get_message() + Return;
ExceptionOutput += "Stack Trace: " + result.get_stackTrace() + Return;
ExceptionOutput += "Exception Type: " + result.get_exceptionType() + Return;
$get("ExceptionInfo").innerHTML = ExceptionOutput;
$get("ExceptionInfo").style.visibility = "visible";
}
}
function CodeCampInfoOnCallBack(result)
{
if ( null != result )
{
if ( result.length == 1 )
{
$get("<%=CityInfo.ClientID%>").innerHTML = result[0].City;
$get("<%=EventDate.ClientID%>").innerHTML = result[0].DateOfEvent;
$get("<%=AttendeeNum.ClientID%>").innerHTML = result[0].NumberOfAttendees;
}
else{
alert(result.get_length() + " number of rows were returned.");
}
}
}
</script>
(2)服务端代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
///<summary>
/// Summary description for GetData
///</summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class GetData : System.Web.Services.WebService
{
public GetData()
{
//Uncomment the following line if using designed components
//InitializeComponent();
}
/*
* [WebMethod]
public string HelloWorld() {
return "Hello World";
}
*/
[WebMethod]
[GenerateScriptType(typeof(CodeCamp))]
public CodeCamp[] CodeCampList()
{
CodeCamp lSpec;
List<CodeCamp> lData = new List<CodeCamp>();
DataTable dtData;
CCData CodeCampData = new CCData();
dtData = CodeCampData.GetCodeCamps();
foreach (DataRow drData in dtData.Rows)
{
lSpec = new CodeCamp(Convert.ToInt32(drData["CodeCampId"]),
Convert.ToString(drData["Name"]));
lData.Add(lSpec);
}
return (lData.ToArray());
}
[WebMethod]
[GenerateScriptType(typeof(CodeCampInformation))]
public CodeCampInformation[] CodeCampInfo(int CodeCampId)
{
CodeCampInformation lSpec;
List<CodeCampInformation> lData = new List<CodeCampInformation>();
DataTable dtData;
CCData CodeCampData = new CCData();
dtData = CodeCampData.CodeCampInfo(CodeCampId);
foreach (DataRow drData in dtData.Rows)
{
lSpec = new CodeCampInformation(Convert.ToString(drData["City"]),
Convert.ToDateTime(drData["DateOfEvent"]),
Convert.ToInt32(drData["NumberOfAttendees"]));
lData.Add(lSpec);
}
//throw (new ApplicationException("Exception in method CodeCampInformation. Data passed: " + Convert.ToString(CodeCampId)));
return (lData.ToArray());
}
[WebMethod]
public DataTable CodeCampInfoWithDelay(int CodeCampId)
{
CCData CodeCampData = new CCData();
System.Threading.Thread.Sleep(10000);
return CodeCampData.CodeCampInfo(CodeCampId);
}
}
需要注意的是在服务端代码中web服务在ajax应用中需要特别添加的是:
1.[ ScriptService]属性;
2.当有负责对象作为数据返回时,这个对象的信息必须要在[GenerateScriptType(typeof())]属性中标明,本例中是[GenerateScriptType(typeof(CodeCamp))]
解析:在客户端,scriptManager包含了需要使用的web服务和脚本文件(本例直接使用脚本代码)。
当页面载入时,pageload函数会被自动调用,pageload()函数是Asp.net Ajax里面特殊函数,如果页面包含此函数将会被页面初始化时自动调用。
function GetCodeCampList()
{
GetData.CodeCampList(CodeCampListOnCompleteCallBack,
CodeCampOnServerException);
}
当调用GetData.CodeCampList()函数时,在web服务中该函数是没有参数的,但是这里设置的两个参数并不会传递给服务端,第一个参数CodeCampListOnCompleteCallBack是web服务调用结束后调用的javascript函数,CodeCampOnServerException是在调用过程中出现错误时调用的客户端方法。
其余的代码就是调用服务端然后返回结果并显示。在这里面已经使用了异步通信方法,当服务端响应完毕时调用某个函数。传统的同步方法有一些弱点:同步过程中页面会被锁定直到服务端响应,静态的页面并不能给用户足够的信息提示,有时候会出现无法响应的情况,这时候用户无法得到信息。
ASP.NET Ajax异步通信包含五个基本的层:
1. Client Application—开发者开发的应用程序。
2. Script core library—本层包含了一些内嵌的模块可以被开发者使用。
3. Client asynchronous communications layer—包含了多种通信方法。
4. Browser compalibility layer—本层包含了对多种主流浏览器的抽象化。
5. The browser itself—浏览器本身。
其中最负责的层就是client asynchronous communications layer.它包含了5个子层:
1. 代理层:包含web服务代理,页面方法代理,个性化代理和验证代理。这些是服务端暴露的服务。
2. WebRequest—WebRequest类是用于创建Http请求。
3. WebRequestManager-此类用于管理web请求和产生请求的对象间的关系(?)。
4. XmlHttpExecutor—此类通过浏览器提供的支持产生网络请求。
5. xmlHttp—此对象是xml http request的底层实现。
和xmlhttpexecutor和xmlhttp同层次的还有JSON序列化器,会在以后提及。
在异步调用过程中数据的传递可以是简单类型,比如整形,字符串型等等,也可以是复杂类型,比如用户自定义类型。Web服务也可以不出现在单调的web服务页面中,也可以压缩在同一个aspx页面中,下面的例子:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services" %>
<script language="c#" runat="server">
[WebMethod]
public static string CallMethod(string UserName)
{
return ("Hi " + UserName);
}
</script>
<!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 runat="server">
<title>Page Methods Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true"></asp:ScriptManager>
<div>
<input type="button" value="Click Me" id="btnClickMe" onclick="CallPageMethod()" />
</div>
</form>
<script type="text/javascript">
function CallPageMethod()
{
PageMethods.CallMethod("Wally", CallPageMethodOnComplete);
}
function CallPageMethodOnComplete(result)
{
alert("Value returned: " + result);
}
</script>
</body>
</html>
需要注意的是:
1. 在<scriptManager>中EnablePageMethod属性需要设置为true.
2. 服务端方法建立时应该冠以[WebMethod]属性并且是静态(static)的.即使服务端代码很可能是对页面是私有的,它也必须被标记为public共有的.
3. 在调用的脚本代码中,要用PageMethods.MethodName来代替ClassName.MethodName.
数据传输格式JSON:
虽然Ajax的命名包含了Xml但是ASP.NET Ajax的数据传输格式却并不是XML而是JSON. JSON是一种数据格式化方法,他的内容有两种方式:
1. 一组值,如数值等.
2. 一组名字/值对的集合,例如对象.
例如类:
public class CodeCampInformation
{
private string _City;
private DateTime _DateOfEvent;
private int _NumberOfAttendees;
public CodeCampInformation()
{
}
public CodeCampInformation(string City, DateTime DateOfEvent, int NumberOfAttendees)
{
_City = City;
_DateOfEvent = DateOfEvent;
_NumberOfAttendees = NumberOfAttendees;
}
public string City
{
get { return (_City); }
set { _City = value; }
}
public DateTime DateOfEvent
{
get { return (_DateOfEvent); }
set { _DateOfEvent = value; }
}
public int NumberOfAttendees
{
get { return (_NumberOfAttendees); }
set { _NumberOfAttendees = value; }
}
}
会被格式化为:
[{"City":"Orlando","DateOfEvent":""/Date(1143302400000)"/","NumberOfAttendees":150}]
如果要更改传输格式为XML可以在web服务中更改以下属性:
[ScriptMethod(ResponseFormat.XML)]
现在我们已经可以调用webservice进行异步操作了,在这种情况下有很多好处,但是在某些情况下可能会出现一些问题。
考虑以下的情况:
1. 有一个服务器控件 drop-downlist(下拉框)。
2. 另一个窗体产生ajax调用并使服务器传回数据。
3. 传回的数据将填充drop-downlist的项。
4. 用户点击一个按键,引发服务器控件单击事件。
当上面的四个步骤完成以后,你就会发现一个异常—非法回传或是回传参数异常,这些异常是因为ASP.NET运行时无法知道下拉框的新值。出现这个问题的原因是ASP.NET运行时试图验证发给服务器的数据,下拉框的值将会被再次和web请求时的值进行验证,当这些值不同时,运行时就会抛出异常。
解决这个问题有下面几种方法:
1.将EnableEventValidation属性设置为false,当设置为false时,将会有安全漏洞,所以应该在需要的页面进行这样的设置。
2. 在页面的Render事件,运行时可以设置控件的允许值。这种方法可能会引起大量数据的保存,从而使ViewState数据库变的很大。方法实例:
Protected override void Render(HtmlTextWriter writer)
{
ClientScript.RegisterForEventValidation(
This.ddlName.UniqueID,”Fred Smith”);
ClientScript.RegisterForEventValidation(
This.ddlName.UniqueID,String.Empty);
Base.Render(writer);
}
3. 采用ASP.NET HTML控件,HTML控件不保存自己的状态,也不会对数据进行验证,将普通HTML控件的属性里面添加 runat=”server”就可以作为ASP.NET HTML控件使用了。
需要注意的是,上述问题在TextBox控件下不会发生。
除此之外,还有一个问题。由于ASP.NET的ViewState机制,可能会引起Ajax异步调用时的值丢失问题,例如:
1. 有一个页面包含一个下拉框控件,ID是ddlName,一个客户端按钮,当用户按下时会引发ASP.NET Ajax异步调用,返回数据“HTML Button”,还有一个服务器控件按钮,会引起页面回发和刷新,返回数据”Server Button”。
2. 当用户点击客户端按钮时,引发Ajax异步调用并将返回值填充下拉框”HTML Button”。
3. 用户再次点击服务器按钮,这时你会发现下拉框原先的“HTML Button“会被刷新掉,并被赋值为他的初始值。
引起这个问题的原因就是ViewState,每次刷新界面,控件的值将会被ViewState重新填充。有两种方法可以解决这个问题:
1. 自己控制状态,你会在客户端将状态序列化并保存在form元素,然后反序列化这些数据并填充到
户端控件中。
2. 使一个在完成Ajax操作之后,只能运行ajax操作,在这种情况下只能使用ajax操作,是一种简单的避免上述问题的方法,当然可能无法满足在页面要求复杂功能的情况。
调用WebService异步操作最后一个需要注意的是错误处理,我们不可能保证异步操作的每一步都能正确运行,调用web服务的格式如下:
WebServiceClass.MethodName(p1,p2,…Pn,OnCompleteCallBack,OnServerExceptionCallBack);
其中p1,p2,…pn是web服务参数,OnCompeleteCallBack是web服务调用完毕后返回数据的处理,最后一个参数OnServerExceptionCallBack就是当服务器调用出现错误时,调用的处理函数。OnServerExceptionCallBack函数是一个JavaScript函数并接受一个异常对象,实例:
function CodeCampOnServerException(result)
{
var ExceptionOutput;
var Return = "<br />";
if ( null != result )
{
for(m in result)
alert(m);
ExceptionOutput = "Message: " + result.get_message() + Return;
ExceptionOutput += "Stack Trace: " + result.get_stackTrace() + Return;
ExceptionOutput += "Exception Type: " + result.get_exceptionType() + Return;
$get("ExceptionInfo").innerHTML = ExceptionOutput;
$get("ExceptionInfo").style.visibility = "visible";
}
}
Result是一个异常对象,他的方法有:
Get_Message();//获得异常错误消息。
Get_stackTrace();//可以跟踪错误堆栈并获得相应信息。
Get_ExceptionType();//获得异常类型信息。
Get_StatusCode();//获得HTTP状态代码
Get_TimedOut();//一个二进制标识代表是否有超时发生。