ASP.NET 3.5核心编程之AJAX Web Service的调用 |
AJAX模型基于两个 品位--客户端 利用程序层和服务器 利用程序层 。在这种模型下,客户端层向服务器层发送 申请,而服务器层向客户端层返回响应 。服务器端点通过URL标识,并通过源(feed)(通常为JSON[JavaScript Object Notation]数据流)向客户端 袒露数据 。服务器层只不过一个 接纳调用并将其转发给 利用程序业务逻辑层的外观 。 下图 描画了整个模型:
为使ASP.NET AJAX页面 可以调用远程服务,该服务必须满足几点要求,其中最 要害的丝毫与端点和底层平台的位置有关 。 支撑AJAX的服务必须位于调用者所处的域中 。这 象征着该服务必须是ASP.NET XML Web服务( 。asmx端点),必须以ASP.NET 利用程序的 模式寄放于同一Web服务器的某个IIS 利用程序中 。 总的来说,关于ASP.NET AJAX 利用服务,有3种定义服务器层服务的 模式: 1. 带有asmx端点的ASP.NET XML Web服务 。 2. 带有svc端点的WCF服务 。 3. 带有aspx端点的页面 步骤,这些 步骤定义在与主调页面 雷同的页面中 。 "服务(Service)"这个词一般被误用 。在AJAX中,服务指的是 附属于 利用程序的代码(位于 利用程序的域中),用于向客户端 袒露相应的 性能 。从 根本上讲,AJAX 利用程序 使用的服务一般不通过 容易对象 拜访 协定(SOAP)进行通讯(而是 使用JSON), 毋庸是面向服务架构(SOA)中自治的服务 。它们与 本身所处平台和域绑定 。 因此,不能称这里的服务为WS-*Web服务和SOA服务 。 REST服务 针对AJAX 利用程序的服务环绕着 袒露给Web客户端数据和资源 。二者可通过HTTP猎取,要求客户端通过URL(也 可以有HTTP标头)来 拜访数据和命令操作 。客户端与服务的交互是通过GET、POST、PUT和DELETE这样的动作来 实现 。换言之,URL用于 形容所要猎取的资源,HTTP动作用于 形容对资源执行的操作 。这类交互过程中 交换的数据由 容易的 格局 示意,甚至 可以用联合 格局(如RSS和ATOM) 示意 。 存在这些 特点的服务为具象状态传输(Representational State Transfer,REST) 。 数据的序列化 AJAX调用包括作为参数传给被调服务 步骤的数据及作为输出返回的数据 。这些数据是如何序列化的? 通讯双方都能 了解的序列化 格局为JavaScript对象 示意法(JSON) 。JSON是一种基于文本的 格局,专门用于在不同 品位间传递对象的状态 。JavaScript 支撑JSON,可通过JavaScript的eval函数将JSON兼容的字符串转换为JavaScript对象 。但是,假如JavaScript字符串代表自定义对象的状态,那么开发者应确保 存在相应类的定义 。 ASP.NET AJAX网络堆栈要负责为每个远程传递的对象 缔造JSON字符串 。在服务器端,通过专门的 格局化程序类 接纳数据,并通过 。NET反射来填充与之匹配的托管类 。在返回时,.NET托管类会被序列化为JSON字符串,并发送给客户端 。脚本治理器会确保 引用这些JSON字符串的类(Web服务代理类)存在于客户端 。 下面给出一个 形容对象状态的JSON 格局的示例: {"ID":"ALFKI", "Company":"Alfred Futterkiste"} 这个字符串 注明该对象有两个属性:ID和Company,存储的是以字符串的 模式序列化的值 。假如某个属性被给予一个非 根本类型的值(如自定义对象),那么该值会以递归 模式序列化为JSON.[ JSON与XML 相比XML,JSON更精炼,更 合适JavaScript语言 。 利用程序特定的Web服务 在默许状况下,ASP.NET Web服务收发的是SOAP数据包(而不是JSON数据包),通过Web服务 形容语言(Web Services Description Language,WSDL)文档来 袒露其协定 。AJAX 利用程序上下文中的ASP.NET XML Web服务是如何工作的呢? 可以通过ASP.NET AJAX 利用程序的web.config文件来 批改 接纳asmx 申请的HTTP 解决程序,将这些调用重定向给 可以 了解JSON流的HTTP 解决程序 。这 象征着ASP.NET XML Web服务 可以是一种双重的服务,即可 承受和 解决SOAP 申请,也可针对JSON 申请 。在配置层,我们 可以禁用SOAP 支撑,并 潜藏用于对外公开该服务 性能的WSDL文件 。 假如要 使用 支撑JSON的ASP.NET Web服务,则需求删除XML,由于在调用ASP.NET Web服务时,我们不 解决SOAP和XML.针对AJAX 利用程序的ASP.NET Web服务不采纳SOAP 信息 。 远程编程接口的定义 协定(contract)用于定义服务器端端点 袒露给调用者的内容 。假如 盼望以ASP.NET Web服务的 模式实现,则不严格要求存在实际的协定 。但假如ASP.NET 3.5中的WCF服务,那么协定就必须存在 。言而总之,以接口 模式设计的公共API会使代码更整洁 。在实现该接口的类 缔造 结束后,有关服务器API接口的工作就 完毕了 。这样我们就 可以公布这个远程API,并使ASP.NET AJAX运行库来治理来自客户端的调用 。 关于ASP.NET Web服务,我们通过 单纯的接口来定义协定,使该接口包括与服务器API有关的 步骤和属性 。下面给出一个 容易的服务: using System;public interface ITimeService{ DateTime GetTime(); string GetTimeFormat(string format);} 这两个 步骤组成了 可以在客户端调用的服务器API. 实现已约定的接口 ASP.NET Web服务通常通过派生自基类WebService的 。NET类来实现: using System.Web.Services;public class TimeService : WebService, ITimeService{ …} 留神,没有必要 定然要从基类WebService派生,这个基类重要用于直接 拜访一些常用的ASP.NET对象(如Application和Session) 。假如不需求直接 拜访这些ASP.NET内部的对象, 即便不从WebService类派生也能 缔造ASP.NET Web服务 。这种状况下,我们可通过HttpContext对象来 直接地 使用ASP.NET内部的对象 。 协定的公布 从 性质来说,公布给定的服务器协定,便是生成一个嵌在页面中的脚本 可以调用的JavaScript代理类 。假如服务器API通过Web服 求实现,我们要向ASP.NET AJAX页面的脚本治理器注册该Web服务 。此外,我们还要在web.config文件中增加一个特别的asmx 申请HTTP 解决程序 。 Web服务的远程调用 Web服务提供了服务器端代码的宿主环境,以便在响应客户端的操作时进行调用 。服务中的Web 步骤指向 利用程序特定的代码 。 AJAX Web服务的 缔造 为ASP.NET AJAX 利用程序定制的Web服务比 其余ASP.NET Web服务要小 。ASP.NET AJAX Web服务与传统的ASP.NET XML Web服务间存在两方面的差别 。 首先,若 使用ASP.NET AJAX Web服务,那么为满足特定 利用程序的需求,我们要设计ASP.NET AJAX Web服务的协定,而不是配置公共服务的行为 。 指标 利用程序便是Web服务的宿主 。其次,我们必须 使用一个新的 特点(attribute)来申明这种Web服务的类,而在 通例的ASP.NET XML Web服务中这是不同意的 。 最终的 动机是,ASP.NET AJAX Web服务可能有两套公共接口:一套是基于JSON对接口,由宿主ASP.NET AJAX 利用程序 使用;另一套是基于SOAP的接口, 袒露给客户端,任何平台都能 拜访该服务的URL. ScriptService 特点(attribute) 为 缔造ASP.NET AJAX Web服务,第一步是要 构建 标准的ASP.NET Web服务 名目,随后导入System.Web.Script.Services命名空间: using System.Web.Script.Services;namespace Core35.WebService{ [WebService(Namespace="http://Core35.book/")] [WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)] [ScriptService] public class TimeService : System.Web.Services.WebService, ITimeService { … }} ScriptService 特点是使ASP.NET XML Web服务与ASP.NET AJAX Web服务间产生差别的 要害 。该 特点指出,该服务旨在 承受来自基于JavaScript客户端代理的调用 。 堵塞SOAP客户端 一旦 缔造AJAX Web服务,便 可以ASMX资源的 模式公布它 。默许状况下,它会有公共的URL, 可以由AJAX客户端调用,同时也能被SOAP客户端和工具发现和 使用 。但我们可禁用SOAP客户端和工具 。为此, 惟独在web.config文件中增加以下设置: <webSevices> <protocols> <clear /> </protocols></webServices> 这段 容易的设置能禁用ASP.NET Web服务定义的全部 协定(包括SOAP),使该服务不得不响应JSON 申请 。 留神,假如增加这些设置,则不 可以通过阅读器的地址栏来调用Web服务,以便进行 容易地测试 。 类似地,我们也不能为URL增加?wsdl后缀来调用WSDL. Web服务 步骤的定义 客户端页面 可以调用Web服务类中带有WebMethod 特点的公共 步骤 。在默许状况下,这些 步骤要通过HTTP动作POST来调用,以JSON对象的 模式返回其值 。我们可通过一个可选 特点ScriptMethod来更改单个 步骤的默许设置 。 ScriptMethod
特点带有3个属性,见下表: 由于 波及安全性和性能问题,因慎重 使用ScriptMethod 特点,下面的代码 使用了该 特点,但未 批改默许设置: [WebMethod][ScriptMethod]public DateTime GetTime(){ …} WebMethod 特点是必选的,而ScriptMethod 特点是可选的 。 AJAX Web服务的注册 为在客户端 发动对ASP.NET Web服务的调用,我们 惟独求XMLHttpRequest、 指标Web服务的URL和JSON流的治理 性能 。为容易起见,全部 性能都包装在映射到远程编程接口的JavaScript代理类中 。该代理类会由ASP.NET AJAX框架自动生成,并注入到客户端 。 为使内建的引擎生成所需的JavaScript代理和辅助类,我们应在需求AJAX Web服务的页面中,向脚本治理器控件注册该Web服务: <asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="'/WebServices/TimeService.asmx" /> </Services></asp:ScriptManager> 关于每个要绑定到页面的Web服务,我们增加一个ServiceReference标签,将Path属性设为对应asmx资源的URL.关于每个服务 引用,都会在客户端自动生成一个额外的<script>块 。该脚本的URL指向一个系统HTTP 解决程序,在内部调用以下URL: '/WebServices/TimeService.asmx/js 追加到Web服务URL的/js后缀 批示ASP.NET AJAX运行库为指定的Web服务生成JavaScript代理类 。假如页面处于调试模板,该后缀会被改为/jsdebug. 默许状况下,JavaScript代理通过<script>标签衔接到页面,这样就需求 径自下载 。通过将ServiceReference对象InlineScript属性设置为true,我们还 可以将任何所需的脚本并入目前页面 。假如启用阅读器缓存,且多个Web页面 使用 雷同的服务 引用,那么默许值false更 合适 。在这种状况下, 不管多少页面需求这个代理类,都 惟独执行一次 申请 。将InlineScript属性设为true会减低网络 申请数,但会多占用 定然的带宽 。 假如以编程 模式注册AJAX Web服务,我们 使用 类似以下的代码: ServiceReference service = new ServiceReference();service.Path = "'/WebServices/TimeService.asmx";ScriptManager1.Services.Add(service); 不管采纳哪种 模式,为调用Web服务,我们 惟独通过JavaScript代理类 发动调用即可 。 使用ASP.NET 利用程序来承托AJAX Web服务 为启用ASP.NET AJAX 利用程序中的Web服务调用,我们需求在web.config文件中增加以下内容以注册一个特别的asmx 申请HTTP 解决程序: <httpHandlers> <remove verb="*" path="*.asmx" /> <add verb="*" path="*.asmx" type="System.Web.Script.Services.ScriptHandlerFactory" /> …</httpHandlers> 该设置已包括在VS2008为 支撑AJAX的Web 名目而 缔造的web.config文件中 。 解决程序工厂(System.Web.Script.Services.ScriptHandlerFactory类)会 取舍负责 解决给定类型 申请的HTTP 解决程序,且能通过Web服务调用中的脚 原来 鉴别JSON调用 。基于JSON的 申请由特别的HTTP 解决程序 解决,而 通例的SOAP调用会 穿梭ASP.NET管道 。 AJAX Web服务的调用 被 引用的ASP.NET AJAX Web服务 袒露给JavaScript代码的类名与服务器类名 雷同 。代理类采纳单例模式, 袒露了外界调用的静态 步骤,无需实例化 。 JavaScript代理类 以上述的timeservice.asmx生成的JavaScript代理类为例,让我们看看它的代码: Type.registerNamespace('Core35.WebServices');Core35.WebServices.TimeService = function(){ Core35.WebServices.TimeService.initializeBase(this); this._timeout = 0; this._userContext = null; this._succceeded = null; this._failed = null;}Core35.WebServices.TimeService.prototype ={ //调用GetTime 步骤 GetTime : function(succeededCallback, failedCallback, userContext) { //invoke参数分别为: //Web Service URL路径 //Web Service 步骤名称 // //传入 步骤的参数数组 //执行 顺利回调函数 //执行失败回调函数 //调用上下文对象 return this._invoke(Core35.WebServices.TimeService.get_path(), 'GetTime', false, {}, succeededCallback, failedCallback, userContext); }, GetTimeFormat : function(timeFormat, succeededCallback, failedCallback, userContext) { return this._invoke(Core35.WebServices.TimeService.get_path(), 'GetTimeAsFormat', false, {format:timeFormat}, succeededCallback, failedCallback, userContext); }}//注册Core35.WebServices.TimeService类,该类继承于Sys.Net.WebServiceProxyCore35.WebServices.TimeService.registerClass('Core35.WebServices.TimeService', Sys.Net.WebServiceProxy);// 缔造一个JavaScript代理类实例Core35.WebService.TimeService._staticInstance = new Core35.WebServices.TimeService(); 在JavaScript中调用WebService 步骤其实是通过最终 缔造的JavaScript代理类实现的: Core35.WebService.TimeService.GetTime = function(onSuccess, onFailed, userContext){ Core35.WebService.TimeService._staticInstance.GetTime(onSuccess, onFailed, userContext);}Core35.WebService.TimeService.GetTimeFormat = function(onSuccess, onFailed, userContext){ Core35.WebService.TimeService._staticInstance.GetTimeFormat(onSuccess, onFailed, userContext);}
path属性用于定义Web服务的URL,我们 可以编程 模式更改该属性值,以便将代理重定向到 其余URL. 远程调用的执行 下面是将JavaScript代理与客户端按钮点击关联的典型 步骤: <input type="button" value="Get Time" onclick="getTime()" /> 按钮最好是客户端按钮,但也 可以是服务器端Button对象生成的提交按钮, 惟独将OnClientClick属性设置为false的JavaScript代码即可,这会幸免它执行默许的回发操作: <asp:Button ID="Button1" runat="server" Text="Button" OnClientClick="getTime(); return false;" /> getTime函数用于采集必要的输入数据,并调用代理类中的静态 步骤 。假如 盼望为回调或消费者上下文对象给予默许值,那么最好在pageLoad函数中进行 。由于pageLoad函数会在客户端页面ASP.NET AJAX 顺利初始化后调用,该函数比阅读器的onload事件更牢靠 。示例代码如下: <script language="javascript" type="text/javascript"> function pageLoad() { //设置默许的调用失败回调函数 Core35.WebServices.TimeService.set_defaultFailedCallback(methodFailed); } function getTime() { Core35.WebServices.TimeService.GetTimeFormat("ddd, dd MMMM yyyy [hh:mm:ss]", methodComplete); } function methodComplete(results, context, methodName) { $get("Label1") 。innerHTML = results; } function methodFailed(errorInfo, context, methodName) { $get("Label1") 。innerHTML = String.Format("Execution of method '{0}' failed because of the following: '{1}'", methodName, errorInfo.get_message()); }</script> 由于Web服务调用是以异步 模式 解决的, 因此我们需求回调来 解决调用 顺利和失败这两种状况 。这两个回调的签名 类似: function method(results, context, methodName) 下表对各参数做了简要
注明:
舛误 解决 failed回调会在服务器上的远程 步骤执行期间 产生 异样时被调用 。在这种状况下,HTTP响应会包括HTTP 舛误码500(内部 舛误) 。 在客户端,服务器 异样通过JavaScript中的Error对象 袒露,该对象会基于从服务器端 获得的 信息和堆栈跟踪而动态 缔造 。Error对象会通过results参数 袒露给failed回调 。我们可通过Error对象的message和stackTrace属性来分别读取收到的 信息和堆栈跟踪 。 假如我们未指派默许的 舛误回调函数,ASP.NET AJAX会调用自己的默许回调函数,该回调函数会弹出一个带有服务器 异样 信息的 信息框 。 为消费者提供反馈 固然UpdatePanel中提供了异步调用的反馈机制(如UpdateProgress控件),但关于传统的远程 步骤调用,我们不得不自行编写代码实现对消费者的反馈 。 我们 可以在远程 步骤调用执行前显示期待 信息、GIF动画或 其余内容: function takeaWhile(){ //显示期待 信息 $get("Feedback") 。innerHTML = "Please, wait …"; Core35.WebServices.MySampleService.VeryLengthyTask(methodCompletedWithFeedback, methodFailedWithFeedback);} 在completed回调中,我们首先重置消费者界面, 而后再进行 其余操作: function methodCompletedWithFeedback(results, context, methodName){ $get("Feedback") 。innerHTML = ""; …} 留神,在 产生 舛误时,我们也要 革除消费者界面 。 超时 解决 假如 发动对asmx Web服务的客户端调用,则是对asmx的直接调用 。关于该 申请,ASP.NET运行库中惟独同步 解决程序 。也便是说, 不管客户端如何检测目前调用是不是正在进行,ASP.NET线程都会被 彻底堵塞,直到AJAX 步骤执行 结束 。为此,我们 可以设置超时 工夫: Core35.WebServices.MySampleService.set_timeout(3000); timeout属性是全局的,作用于代理类的全部 步骤 。 假如 申请超时,我们便不会从服务器收到响应,客户端不得不单方面的撤销执行 。 |