• 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"
    
     
    
    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <Add xmlns="http://tempuri.org/">
    
          <x>33</x>
    
          <y>66</y>
    
        </Add>
    
      </soap:Body>
    
    </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/AddSubTract的值是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"
    
    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <Subtract xmlns="http://example.org/math">
    
          <x>33</x>
    
          <y>66</y>
    
        </Subtract>
    
      </soap:Body>
    
    </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: ""
    
    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <Add xmlns="http://example.org/math">
    
          <x>33</x>
    
          <y>66</y>
    
        </Add>
    
      </soap:Body>
    
    </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元素,如下所示:

    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <Distance xmlns="urn:geometry">
    
          <orig>
    
            <x>0</x>
    
            <y>0</y>
    
          </orig>
    
          <dest>
    
            <x>3</x>
    
            <y>4</y>
    
          </dest>
    
        </Distance>
    
      </soap:Body>
    
    </soap:Envelope>
    

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

    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <DistanceResponse
    
          xmlns="urn:geometry">
    
          <DistanceResult>5</DistanceResult>
    
        </DistanceResponse>
    
      </soap:Body>
    
    </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请求消息如下:

    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <CalcDistance xmlns="urn:geometry">
    
          <o x="0" y="0" />
    
          <d x="3" y="4" />
    
        </CalcDistance>
    
      </soap:Body>
    
    </soap:Envelope>
    

    并将产生下面的应答消息:

    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Body>
    
        <CalculatedDistance xmlns="urn:geometry">
    
          <result>5</result>
    
        </CalculatedDistance>
    
      </soap:Body>
    
    </soap:Envelope>
    

    .asmx句柄使用SOAP document/literal风格来实现和描述上面显示的默认映射。意思上说WSDL定义将包含literal XML schema定义,它描述了SOAP消息中用到的请求和响应元素。

    .asmx句柄也能使用SOAP rpc/encoded风格。这意味着SOAP体中包含一个RPC调用的XML代表(representation),参数用SOAP编码规则来串行化。实现这些仅需将[SoapDocumentService] and [SoapDocumentMethod]替换为[SoapRpcService] and [SoapRpcMethod]属性。

    正如你所看到的,我们可能完全定制一个从给定方法到SOAP消息的映射。XmlSerializer提供了一个强大的串行化引擎。

    除了处理参数的反串行化,.asmx句柄也能串行化/反串行化SOAP头。SOAP头的处理不同于参数,因为它们被认为是典型的无法控制的信息,和具体的方法没有直接的联系。由于这些,头处理主要是通过中间层(interception layers),完全为WebMethods屏蔽了头处理。

    然而如果想涉足于WebMethod中的头信息,你必须提供一个.NET类,从SoapHeader派生而来,它代表了头的XML schema类型。然后定义一个此类型的成员变量作为每一个头实例的占位符。最后标记每个要访问头的WebMethod,指定你想要到达的域名。

    比如,下面的SOAP请求包括一个用来进行身份验证的UsernameToken头。

    <soap:Envelope
    
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    
    >
    
      <soap:Header>
    
        <x:UsernameToken xmlns:x="http://example.org/security">
    
          <username>Mary</username>
    
          <password>yraM</password>
    
        </x:UsernameToken>
    
      </soap:Header>
    
      <soap:Body>
    
        <CalcDistance xmlns="urn:geometry">
    
           ...
    
  • 相关阅读:
    bug案例 php 生成csv文件 下载打开文件 提示 文件格式与文件扩展名指定的格式不一致 提示 是SYLK 文件解决方法
    php 三元运算 ?: 和 ?? 认识
    mysql 索引知识
    算法 之 红黑平衡二叉树
    算法 计算机中树的分类 概念树
    mysql B+树
    算法 之 二叉树的前序 中序 后序遍历
    mysql B树分析
    mysql 坑位之 GROUP_CONCAT 函数使用
    HTTP请求:GET与POST方法的区别
  • 原文地址:https://www.cnblogs.com/ymyglhb/p/1263452.html
Copyright © 2020-2023  润新知