深入Atlas系列:Web Sevices Access in Atlas(8) - RTM中可叹的Web Service Proxy
版权声明:原创作品,如需转载,请与作者联系。否则将追究法律责任。 |
在RTM Release之前,我已经差不多将Web Service Proxy的分析写完了,可惜一个“惊天地泣鬼神”的RTM一出,这片文章的诞生晚了20多天。 使用Web Service Proxy应该是使用ASP.NET AJAX访问Web Service最常用的方法了。服务器端会根据ScriptManager中添加的Service引用而对于Web Service类进行分析,并生成相应的客户端脚本。这样开发人员就能在客户端方便而且直观地访问Web Services方法了。这是ASP.NET中很重要的功能。 从官方文档上看来,CTP和RTM似乎在脚本使用这方面没有很大的改 变,只要在服务器端将一些CustomAttribute改变一下就可以了。的确没错,在使用方式上只有这点细微改变,但是事实上,从生成脚本本身来说, CTP和RTM的做法大相径庭。它们的之间的区别主要有两点:
至于第2点,个人认为是配合了目前的Web Serivce访问方式而改变的。它遵守了以下的规范和特点:
首先,我们在CTP和RTM中定义一个相同的复杂类型:Employee。代码如下: namespace Jeffz.WebServicesProxyDemo
{ public class Employee { public string Name; public int Age; } } 再为CTP定义一个Web Service,其中有一个GetEmployee方法。代码如下: namespace Jeffz.WebServicesProxyDemo
{ public class EmployeeService : WebService { [WebMethod] public Employee GetEmployee(string name, int age) { Employee emp = new Employee(); emp.Name = name; emp.Age = age; return emp; } } } 然后在CTP的页面中添加ScriptManager定义。如下: <atlas:ScriptManager runat="server" ID="ScriptManager1">
<Services> <atlas:ServiceReference InlineProxy="true" Path="EmployeeService.asmx" /> </Services> </atlas:ScriptManager> InlineProxy设为true,则会把这些Proxy脚本输出在页面中,而不是在页面中通过<script />添加一个脚本引用。而输出的脚本也只有寥寥数行,如下: Type.registerNamespace('Jeffz.WebServicesProxyDemo');
Jeffz.WebServicesProxyDemo.EmployeeService = new function() { this.appPath = "http://localhost:2481/Atlas-CTP/"; var cm = Sys.Net.ServiceMethod.createProxyMethod; cm(this, "GetEmployee", "name", "age"); } Jeffz.WebServicesProxyDemo.EmployeeService.path = '/Atlas-CTP/WebServicesProxyDemo/EmployeeService.asmx'; 可以看出,这里的一个关键,就是Sys.Net.ServiceMethod.createProxyMethod,它会为Jeffz.WebServiceProxyDemo.EmployeeService对象添加一个GetEmployee方法。代码如下: // 访问该方法时:
// Sys.Net.ServiceMethod.createProxyMethod( // proxy, methodName, argName1, argName2, ...) Sys.Net.ServiceMethod.createProxyMethod = function(proxy, methodName) { var numOfParams = arguments.length - 2; var createWebMethodArguments = arguments; // 下面就是方法(例如“GetEmployee”)的真正定义 proxy[methodName] = function() { // 就是大名鼎鼎的params对象,传递给服务器端方法的参数 var args = {}; for (var i = 0; i < numOfParams; i++) { // 参数不能是function if (typeof(arguments[i]) == 'function') { throw Error.createError( String.format( "Parameter #{0} passed to method '{1}' should not be a function", i + 1, methodName)); } // 一一指定参数 args[createWebMethodArguments[i + 2]] = arguments[i]; } // 传递给Sys.Net.ServiceMethod.invoke方法的参数 var callMethodArgs = [ proxy.path, methodName, proxy.appPath, args ]; // 准备callMethodArgs内的各个callback,priority,userContext等。 for (var i = 0; i + numOfParams < arguments.length; i++) { callMethodArgs[i + 4] = arguments[numOfParams + i]; } // 调用Sys.Net.ServiceMethod.invoke方法 return Sys.Net.ServiceMethod.invoke.apply(null, callMethodArgs); } } Proxy就是这样生成的。可以看出,在这里 Jeffz.WebServicesProxyDemo.EmployeeService像是服务器端对应的Web Service类的一个客户端实例,而且还是个Singleton。这个实例中存在着一个个方法,使用这些方法能够方便地调用服务器端的Web Service方法。 与CTP不同的是,RTM中由于每个Web Service类的功能增多(例如增加了defaultSuccessCallback属性等),因此在客户端输出了一个完整的Web Service类的定义,然后在初始化一个Singleton实例。再将我们需要使用的方法定义成Static方法,在Static方法中会将功能委托给 Singleton实例的相应方法。具体如下: 依旧使用上面的Employee类与GetEmployee方法(在GetEmployee方法总必须加上ScriptServiceAttribute标记)。ScriptManager的使用稍微有些变化。如下: <asp:ScriptManager runat="server" ID="ScriptManager1" ScriptMode="Debug">
<Services> <asp:ServiceReference InlineScript="true" Path="EmployeeService.asmx" /> </Services> </asp:ScriptManager> InlineScript的作用和之前的InlineProxy相同。请注意这里我将ScriptManager的ScriptMode设为了Debug,这样页面上生成的代码会相对容易阅读,并且增加了必要的注释和参数验证代码。 首先是注册了命名空间和构造函数: Type.registerNamespace('Jeffz.WebServicesProxyDemo');
Jeffz.WebServicesProxyDemo.EmployeeService=function() { this._timeout = 0; this._userContext = null; this._succeeded = null; this._failed = null; } 然后就是使用prototype来定义类的方法了。可以看到它定义GetEmployee方法的脚本: Jeffz.WebServicesProxyDemo.EmployeeService.prototype =
{ GetEmployee: function(name,age,succeededCallback, failedCallback, userContext) { /// <summary>Invoke the GetEmployee WebMethod</summary> /// <param name="name">WebMethod parameter: name(type: String)</param> /// <param name="age">WebMethod parameter: age(type: Int32)</param> /// <param name="succeededCallback" type="function" optional="true">Callback on successful completion of request</param> /// <param name="failedCallback" type="function" optional="true">Callback on failure of request</param> /// <param name="userContext" optional="true">User context data (any JavaScript type)</param> return Sys.Net._WebMethod._invoke.apply ( null, [ this, 'GetEmployee', 'Jeffz.WebServicesProxyDemo.EmployeeService.GetEmployee', false, {name : name, age : age}, succeededCallback, failedCallback, userContext ]); }, …… } 可以看出,Proxy使用了Sys.Net._WebMethod._invoke方法访问服务器端,并将this传递作为proxy变量传 递给该方法。将自身作为proxy传递给Sys.Net._WebMethod是ASP.NET AJAX客户端脚本中大量使用的方法,这个方法有个好处就是能够集中地管理proxy的功能,因为这些功能往往只和某个Web Service方法的访问有关。但是如果需要共享proxy的话,那么只有另外定义一个proxy对象了。 接下来就是对于proxy一些必须的方法的定义了,如下: 然后建立一个Singleton对象,所有的工作将由该对象完成: Jeffz.WebServicesProxyDemo.EmployeeService._staticInstance = new Jeffz.WebServicesProxyDemo.EmployeeService();
接着定义Jeffz.WebServicesProxyDemo上的静态方法,并将实现委托给Singleton对象的相应方法。如下: 最后,设置一下Web Service方法需要的路径: Jeffz.WebServicesProxyDemo.EmployeeService.set_path("/Value-add-WebSite/WebServicesDemo/EmployeeService.asmx");
当然,也不要忘了最重要的事情:将静态GetEmployee方法的功能委托给Singleton对象的GetEmployee方法完成。如下: Jeffz.WebServicesProxyDemo.EmployeeService.GetEmployee = function(name, age, onSuccess, onFailed, userContext)
{ /// <summary>Invoke the GetEmployee WebMethod</summary> /// <param name="name">WebMethod parameter: name(type: String)</param> /// <param name="age">WebMethod parameter: age(type: Int32)</param> /// <param name="succeededCallback" type="function" optional="true">Callback on successful completion of request</param> /// <param name="failedCallback" type="function" optional="true">Callback on failure of request</param> /// <param name="userContext" optional="true">User context data (any JavaScript type)</param> Jeffz.WebServicesProxyDemo.EmployeeService._staticInstance.GetEmployee(name, age, onSuccess, onFailed, userContext); } 可以看到,虽然同样是使用Jeffz.WebServicesProxyDemo.EmployeeService.GetEmployee 访问Web Service方法,但是CTP和RTM版本中的逻辑可以说相差了很多。虽然RTM版本生成的脚本多出许多,还好有了Cache机制,性能上不会有什么影 响,而且RTM的功能也增强了。除去“自定义能力”的丢失之外,RTM的Web Service访问的确有了进步。 至于生成Employee类的客户端脚本,CTP和RTM几乎一模一样,而且总共只有寥寥数行,在这里就不多加说明了。 本文出自 “赵劼” 博客,转载请与作者联系! 本文出自 51CTO.COM技术博客 |



jeffz
博客统计信息
热门文章
最新评论
友情链接
