开发自己的Web服务处理程序(以支持Ajax框架异步调用Web服务方法)

当你添加Asp.net AJAX功能到你的Web程序的时候,你需要在Web.config中做一些改变,需要你显式地移除默认的ASMX处理程序并且添加asp.net ajax框架自己的脚本处理器来作为ASMX处理程序。在上一篇异步调用Web服务方法中,我们谈论过,ajax框架的asmx(ScriptHandler)是不支持异步调用Web服务方法的,所以为了让asp.netajax支持异步Web方法调用,我们需要避开该处理器,以提供自定义的处理器来取代它。

 

Asp.netAJAX框架的ASMX处理器——ScriptHandler

ScriptHandler是一个常规的HTTP处理程序,它能够通过解析URL找出调用了哪个Web服务及Web方法。然后对应于Web服务类型通过反射来执行Web方法。调用Web方法所涉及的步骤如下:

1、  通过检查Content-Type来确认这是一个基于Ajax的Web方法调用,并查看它是否有application/json的属性。如果没有,则激活一个异常。

2、  通过解析请求的URL找出哪个.asmx被调用,返回.asmx文件类型且被编译过的程序集。

3、  反射程序集并找出Web服务和被调用的Web方法

4、  反序列化输入参数到适当的数据类型中,以防Http POST反序列化JSON图表

5、  查看方法中的参数并映射每个参数到JSON反序列化的对象上

6、  初始化缓存策略

7、  通过反射调用方法并且传递与JSON匹配的参数值

8、  获取返回值。序列化返回值到JSON/XML

9、  发射JSON/XML作为响应


基本的异步Web服务处理程序

首先你需要一个Http处理程序,它将拦截所有对Web服务的调用。在Web.config的<httphandlers>节点下你需要映射该处理程序到*.asmx来进行扩展。默认情况下,asp.net ajax将映射到ScriptHandler来处理*.asmx扩展名类型的文件,因此需要用你自己的Http处理程序来取代它。

在提供的代码中,ASMXHttpHandler.cs是主要的HTTP处理程序类。ASMXHttpHandler类实现了IhttpAsyncHandler接口。调用Web服务期间,当该处理程序被调用的时候,asp.net框架会首先调用BeginProcessRequest方法。在该方法中,处理程序会解析锁请求的URL并找出调用了哪个Web服务的Web方法。


WebServiceDef类对Type类进行了包装,同时还包含Web服务类型的一些信息。它维护了WebMethodDef类的集合项,其中每项都包含对某个Web方法的定义。WebMethodDef类为每个方法,与方法相关的特性、是否支持Http GET方式以及如果可能,对Begin和End方法对的引用都提供了一个名称。如果没有Begin和End方法对,该方法会如下例代码所示,以同步方式执行。所有的这些类都作为Web服务和Web方法进行缓存处理,因此不需要重复使用反射查看元数据。


当方法同步执行的时候,BeginProcessRequest将立即返回。它将返回一个AsmxHandlerSyncRequest实例,表示请求以按同步方式执行并且不需要出发EndProcessRequest方法。AsmxHandlerSyncRequest类实现了IasyncRequest接口。它会从CompletedSynchronously属性返回真值。


回到BeginProcessRequest方法上,当存在Begin和End方法对的时候,它会调用Web方法的BeginXXX方法并从该方法返回。执行到Asp.net框架并返回线程到线程池。

下面的代码展示了调用Web服务上BeginXXX方法的准备步骤。首先,除了最后两个参数(一个是AsyncCallback,另一个是某个对象状态),来自请求的所有参数都需要进行适当的映射。


注:Web服务从System.Web.Services.WebService命名空间继承,并实现了Idisposable接口。Activator.CreateInstance是.net框架类的一个实例,它能从自己的类型中动态实例化任何类并返回一个对象的引用。在上例代码中,Web服务类实例被创建,并且使用了对Idisposable接口的引用。使用Idisposable接口是因为当我们调用结束后需要释放占用的资源。


一旦准备工作完成,BeginXXX方法会被调用。现在BeginXXX方法能同步执行并立即返回。在这种情况下,你必须完整地产生BeginXXX方法的响应并完成该请求的执行。但是,如果BeginXXX方法需要更多的时间来异步执行的话,你需要为Asp.net框架返回该执行以便它把线程返回给线程池。当异步操作完成后,EndProcessRequest方法将执行回调并继续处理该请求。


当异步操作完成并且回调被激活的时候,EndProcessRequest方法也被激活。例如,如果你异步调用某个外部Web服务其里面包含了命名为BeginXXX的Web方法,则你需要传递一个对AsyncCallback的引用。这会为你接受到的BeginProcessRequest执行同样的回调。Asp.net框架会给你创建一个回调的引用,你可以在HTTP处理程序中用Web服务的EndXXX方法来获得响应和产生输出。


当EndXXX类的Web方法调用完成后,如果该方法不是一个返回空类型的方法,你将得到一个返回值,在这种情况下,你需要把该返回值转换到某个JSON格式的字符串中并返回到浏览器端。然而,该方法也可以返回某个XML格式的字符串而取代JSON格式。因此,仅需要把这些字符串写入到HttpResponse对象即可。




为Web方法添加事务化的能力

到此为止,Web方法的执行并没有支持事务。可以使用[TransactionalMethod]特性来界定事务包括的范围、代码的隔离级别和超时时段。

带有TransactionalMethod特性的Web方法将在事务块中自动执行。这里我们将使用.net 2.0事务。事务管理完全在HTTP处理程序中执行并且Web方法并不需要做过多事情。当Web方法遇到异常的时候,事务会自动回滚;否则,事务将自动提交。

ASMXHttpHandler的ExecuteMethod方法同步调用Web方法并提供了事务支持。目前,由于从一个线程到另一个线程的不断转换,针对异步方法的事务支持还没有实现。所以TransactionScope从本地线程存储中的特性丢失了。


上例代码展示了一个执行得当的Web方法并产生一个响应。该Web方法在一个定义为TransactionalMethod特性的事务范围块内执行。但是当Web方法抛出一个异常的时候,它会定位到出现异常消息块的地方。最终,TransactionScope被释放并会检查本次操作是否已经提交。如果没有提交,TransactionScope会回滚该事务。


整个事务化管理都位于HTTP处理程序中,因此,不需要担心Web服务中的事务。仅需要添加一个特性,然后该Web方法就具有了事务特性。

添加缓存头

Asp.net Ajax框架在调用Web方法之前会进行缓存策略初始化操作。如果你没有在[WebMethod]属性中设置缓存持续时间,那么它将把MaxAge设置为零。一旦MaxAge被设置为零,就不能再对它增加。因此,为了从浏览器端获取缓存响应,你不能动态地从你的Web方法代码中增加MaxAge的值。而由于HttpCachePolicy的限制,一旦设定MaxAge的值,就不能再对它进行增加。此外,如果你使用Http检测工具来查看从Web服务调用返回的响应,你将会看到该响应丢失了Content-Length特性。没有该特性,浏览器不能使用Http管道,这将很大滴提升Http响应的下载时间。

 

为了处理缓存策略,在GenerateResponse方法中作了一些补充。我们的想法是该Web方法在HttpResponse对象中已经设置了一些缓存策略,因此它将不会改变任何缓存设置。否则,它会进行缓存设置检查是否应用了WebMethod特性,然后设置缓存头。



IsCacheSet方法检查在一些通用的缓存设置中是否有任何变化。如果发生变化,Web方法本身需要对缓存进行处理(由框架完成),并且GenerateResponse方法对于缓存策略并没有发生任何改变。


使用

只需在Web.config中将ScriptHandler处理器用该处理器替代即可。


源代码下载

posted @ 2011-12-04 18:30  程序员天下  阅读(309)  评论(0编辑  收藏  举报