ASP.NET Web Service如何工作(2)

HTTP 管道一旦调用了 .asmx 句柄,便开始了 XML 、 XSD 、 SOAP 和 WSDL 的处理。 .asmx 句柄提供的余下的功能被分为三个领域:

消息分派

当 .asmx 句柄被 HTTP 管道调用时,通过查看 .asmx 文件中的 WebService 声明,确定检查哪个 .NET 类。然后它观察到来的 HTTP 消息中的信息,确定调用引用类中的哪个方法。为了调用前面例子中的 Add 方法, HTTP 请求消息应像下面一样:

POST /math/math.asmx HTTP/1.1


Host: localhost


Content-Type: text/xml; charset=utf-8


Content-Length: length


SOAPAction: "http://tempuri.org/Add"
 1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 2    
 3    
 4      <soap:body>
 5    
 6    
 7        <add xmlns="http://tempuri.org/">
 8    
 9    
10          <x>33</x>
11    
12    
13          <y>66</y>
14    
15    
16        </add>
17    
18    
19      </soap:body>
20</soap:envelope>

上面的 HTTP 请求消息中有两条信息可以用来确定调用类中的哪个方法: SOAPAction 头或 soap 体中请求元素的名字。在这个例子中,每种方法都指出了发送者想调用的方法名。

.asmx 句柄使用 SOAPAction 头的值来实现消息的分派。因此, .asmx 句柄查看消息中的 SOAPAction 头,使用 .NET 映射检查引用类中的方法。它只考虑标记了 [WebMethod] 属性的方法,但通过查看每种方法的 SOAPAction 值再具体确定调用哪个方法。因为我们在类中并没有明确的指定 SOAPAction 的值, .asmx 句柄认为 SOAPAction 的值是 Web 服务的名称空间加上方法名。而且我们也没有指定名称空间,所以句柄就把 http://tempuri.org 作为默认值。这样 Add 方法的默认 SOAPAction 值就是 http://tempuri.org/Add

可以按如下方法定制 Web 服务的名称空间。把类标记上 [WebService] 属性,用 [SoapDocumentMethod] 属性标记 WebMethods 来指定具体的 SOAPAction 值。示例如下:

using System.Web.Services;


using System.Web.Services.Protocols;


 


[WebService(Namespace="http://example.org/math")]


public class MathService


{


   [WebMethod]


   public double Add(double x, double y) {


      return x + y;


   }


   [WebMethod]


   [SoapDocumentMethod(Action="urn:math:subtract")]


   public double Subtract(double x, double y) {


      return x - y;


   }


   ...


}

现在 .asmx 句柄认为 Add 方法的 SOAPAction 值是 http://example.org/math/Add ** , ** SubTract 的值是 urn:math:subtract (因为我们在类中明确定义了)。比如下面的 HTTP 请求消息调用 Subtract :
POST /math/math.asmx HTTP/1.1

Host: localhost


Content-Type: text/xml; charset=utf-8


Content-Length: length


SOAPAction: "urn:math:subtract"
 1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 2    
 3    
 4      <soap:body>
 5    
 6    
 7        <subtract xmlns="http://example.org/math">
 8    
 9    
10          <x>33</x>
11    
12    
13          <y>66</y>
14    
15    
16        </subtract>
17    
18    
19      </soap:body>
20</soap:envelope>

如果 .asmx句柄没为HTTP请求消息找到一个SOAPAction匹配,将会抛出一个异常。如果你不想依赖SOAPAction头来分派消息,可以引导.asmx句柄使用请求元素名称。采用这种方法需要为类标记上[SoapDocumentService]属性的RoutingStyle特性,同时也应该指出WebMethods不需要SOAPAction值(在类中设定其值为空)。如下所示:

using System.Web.Services;


using System.Web.Services.Protocols;


 


[WebService(Namespace="http://example.org/math")]


[SoapDocumentService(


  RoutingStyle=SoapServiceRoutingStyle.RequestElement)]


public class MathService


{


   [WebMethod]


   [SoapDocumentMethod(Action="")]


   public double Add(double x, double y) {


      return x + y;


   }


   [WebMethod]


   [SoapDocumentMethod(Action="")]


   public double Subtract(double x, double y) {


      return x - y;


   }


    ...


}

在这种情况下,句柄甚至不关心 SOAPAction 的值,它使用请求元素的名字确定调用方法。比如,在下面的 HTTP 请求消息中,它希望调用 Add 方法的请求元素的名字是 Add :

POST /math/math.asmx HTTP/1.1


Host: localhost


Content-Type: text/xml; charset=utf-8


Content-Length: length


SOAPAction: ""
 1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 2    
 3    
 4      <soap:body>
 5    
 6    
 7        <add xmlns="http://example.org/math">
 8    
 9    
10          <x>33</x>
11    
12    
13          <y>66</y>
14    
15    
16        </add>
17    
18    
19      </soap:body>
20</soap:envelope>

所以当 .asmx 句柄接收到 HTTP 消息时它要做的第一件事情就是决定如何分派消息到对应的 WebMethod 。在它真正调用方法之前,还需要将到来的 XML 映射到 .NET 对象。

将 XML 映射到对象

一旦 WebMethod 句柄决定了调用哪个方法,它就会将 XML 消息反串行化为 .NET 对象。随着消息分派,句柄通过 reflection 检查类,然后决定怎样处理 XML 消息。 XmlSerializer 类自动完成 XML 和 System.Xml.Serialization 名称空间中类的映射。

XmlSerializer 能实现任何 .NET 公共类型到 XML Schema 类型的映射,有了这个适当的映射,它能自动的实现 .NET 对象和 XML 实例文档的映射(见图 4 )。 XmlSerializer 受 XML Schema 所支持功能的限制,虽不能处理所有复杂的现代对象模型(如非树型的对象图),却能处理开发者常用的复杂类型。

再看前面 Add 的例子, XmlSerializer 将把 x 和 y 元素映射为 .NET 的 double 值(调用 Add 方法时必须提供的)。 Add 方法返回一个 double 类型值给调用者,这也需要被串行化为 SOAP 应答消息中的一个 XML 元素。

** Figure 4. Mapping XML to objects **

XmlSerializer 也能自动处理一些复杂类型(除了上面说到的一些限制)。比如,下面的 WebMethod 计算两个点结构之间的距离。

using System;


using System.Web.Services;


public class Point {


  public double x; 


  public double y;


}


[WebService(Namespace="urn:geometry")]


public class Geometry {


  [WebMethod]


  public double Distance(Point orig, Point dest) {


    return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) +


                     Math.Pow(orig.y-dest.y, 2));


  }


}

请求此操作的 SOAP 消息将包含一个 Distance 元素,它包含了两个子元素,一个称作 orig ,另一个是 dest ,每一个都包括了 x 和 y 元素,如下所示:

 1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 2    
 3    
 4      <soap:body>
 5    
 6    
 7        <distance xmlns="urn:geometry">
 8    
 9    
10          <orig>
11    
12    
13            <x>0</x>
14    
15    
16            <y>0</y>
17    
18    
19          </orig>
20    
21    
22          <dest>
23    
24    
25            <x>3</x>
26    
27    
28            <y>4</y>
29    
30    
31          </dest>
32    
33    
34        </distance>
35    
36    
37      </soap:body>
38</soap:envelope>

这种情况下 SOAP 应答消息将包含一个 DistanceResponse 元素,它包含一个 double 类型的 DistanceResult 子元素。

 1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 2    
 3    
 4      <soap:body>
 5    
 6    
 7        <distanceresponse xmlns="urn:geometry">
 8    
 9    
10          <distanceresult>5</distanceresult> 
11    
12    
13        </distanceresponse>
14    
15    
16      </soap:body>
17</soap:envelope>

缺省的 XML 映射使用方法名作为请求元素名,参数名作为子元素名。每个参数的结构依赖于类型的结构。公共字段和属性的名字简单映射为子元素,如 Point 类中的 x 和 y 。应答元素的名字缺省为请求元素的名字后面附加上“ Response ” , 应答元素也包含一个子元素,是请求元素名字后面附加“ Result ”。也有可能使用一些固定的映射属性来打破标准的 XML 映射。比如,你可以使用 [XmlType] 属性来定制类型的名字和名称空间,使用 [XmlElement] 和 [XmlAttribute] 属性来控制如何将参数或类成员分别映射为元素或属性,也可以使用 [SoapDocumentMethod] 属性控制怎样把方法本身映射为请求 / 响应消息中的元素名。比如,检查下面重新定义的 Distance 。

using System;


using System.Web.Services;


using System.Web.Services.Protocols;


using System.Xml.Serialization;


 


public class Point {


  [XmlAttribute]


  public double x;


  [XmlAttribute] 


  public double y;


}


[WebService(Namespace="urn:geometry")]


public class Geometry {


  [WebMethod]


  [SoapDocumentMethod(RequestElementName="CalcDistance",


     ResponseElementName="CalculatedDistance")]


  [return: XmlElement("result")]


  public double Distance(


    [XmlElement("o")]Point orig, [XmlElement("d")]Point dest) {


    return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) +


                     Math.Pow(orig.y-dest.y, 2));


  }


}

它所期望的 SOAP 请求消息如下:

1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
2    
3    
4    &lt;SPAN lang=EN-U</soap:envelope>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus