XmlSerializer也能自动处理复杂的类型(除了上面描述的限制)。例如,下面的WebMethod计算两个Point(点)结构体之间的距离。
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.
>
<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元素,该元素包含一个双精度类型的DistanceResult元素:
<soap:Envelope
xmlns:soap="http://schemas.
>
<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));
}
}
这个版本的Distance要求输入的SOAP消息格式如下:
<soap:Envelope
xmlns:soap="http://schemas.
>
<soap:Body>
<CalcDistance xmlns="urn:geometry">
<o x="0" y="0" />
<d x="3" y="4" />
</CalcDistance>
</soap:Body>
</soap:Envelope>
它生成的SOAP响应消息的格式如下:
<soap:Envelope
xmlns:soap="http://schemas.
>
<soap:Body>
<CalculatedDistance xmlns="urn:geometry">
<result>5</result>
</CalculatedDistance>
</soap:Body>
</soap:Envelope>
该.asmx处理程序使用SOAP document/literal样式来实现和描述上面所示的默认映射。这意味着该WSDL定义将包含描述SOAP消息中所使用的请求和响应元素的字面上的XML schema定义(例如,没有使用SOAP编码规则)。
该.asmx处理程序也可以使用SOAP rpc/encoded样式。这意味着SOAP主体(Body)包含一个RPC调用的XML表现,并且参数都使用SOAP编码规则串行化了(例如,不需要XML Schema)。为了达到这个目标,使用[SoapRpcService]和[SoapRpcMethod]代替[SoapDocumentService]和[SoapDocumentMethod]标志。如果你要了解这些样式之间的差别,请参阅Understanding SOAP。
从上面的信息中你可以发现,完全地自定义如何把给定的方法映射到SOAP消息是可能的。XmlSerializer提供了强大的串行化引擎以及许多我们在此文中没有讨论的特性。如果你要了解XmlSerializer如何工作的详细信息,请查阅Moving to .NET and Web Services。
作为处理参数的并行化的补充,.asmx处理程序也能够并行化/串行化SOAP头部。SOAP头部的处理方法与参数不同,因为典型情况下它们被认为是范围之外的信息,没有直接关联到某个特定的方法。由于这个原因,典型的头部处理是由监听层完成的,使WebMethod根本不用进行头部处理。
但是,如果你希望在WebMethod中处理头部信息,你必须提供一个演示自SoapHeader(它描述了头部的XML Schema类型)的.NET类。接着你定义该类型的一个成员变量作为头部实例的位置标志符。最后,你给每个需要访问该头部的WebMethod作注解,指定你需要处理的字段的名称。
例如,看一看下面的包含用于身份验证目的的UsernameToken头部的SOAP请求:
<soap:Envelope
xmlns:soap="http://schemas.
>
<soap:Header>
<x:UsernameToken xmlns:x="http://
<username>Mary</username>
<password>yraM</password>
</x:UsernameToken>
</soap:Header>
<soap:Body>
<CalcDistance xmlns="urn:geometry">
...
为了使.asmx处理程序能够并行化该头部,首先你必须定义一个描述隐含的XML Schema类型的.NET类(注意:如果你已经有了该头部的XML Schema,那么可以使用xsd.exe /c生成这个类)。在这种情况下,相应的类如下所示:
[XmlType(Namespace="http://)]
[XmlRoot(Namespace="http://)]
public class UsernameToken : SoapHeader {
public string username;
public string password;
}
接着,你必须在WebMethod类中简单地定义一个成员变量来保持该头部类的一个实例,并使用[SoapHeader]标志来注解该WebMethod,如下所示:
using System;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace="urn:geometry")]
public class Geometry {
public UsernameToken Token;
[WebMethod]
[SoapHeader("Token")]
public double Distance(Point orig, Point dest) {
if (!Token.username.Equals(Reverse(Token.password)))
throw new Exception("access denied");
return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) +Math.Pow(orig.y-dest.y, 2));
}
}
接着,在WebMethod中你可以访问该头部提供的Token字段并提取信息。你也可以使用相同的技术把该头部送回给客户端--你只需要在[SoapHeader]标志声明中简单地指定头部的方向。需要了解在WebMethod框架组件中处理SOAP头部的更多信息,请查阅Digging into SOAP Headers with the .NET Framework。
.asmx处理程序也提供了.NET异常的自动的串行化。任何被.asmx捕捉到的没有处理的异常都自动地串行化进入响应中的SOAP Fault元素。例如,在前面的例子中,如果用户名与与密码不匹配,代码将抛出一个.NET异常。接着.asmx处理程序捕捉到这个异常并串行化到下面所示的SOAP响应中:
<soap:Envelope xmlns:soap="http://schemas.
<soap:Body>
<soap:Fault>
<faultcode>
soap:Server
</faultcode>
<faultstring>
Server was unable to process request. --&gt; access denied
</faultstring>
<detail />
</soap:Fault>
</soap:Body>
</soap:Envelope>
如果你需要更多地SOAP Fault元素控制权,可以明确地抛出一个SoapException对象,指定所有的SOAP Fault元素细节,例如faultcode、faulstring、faultactor和detail元素。你可以参阅Using SOAP Faults获得更多信息。
如上所示,指出WebMethod如何工作必须了解下层串行化引擎和它的多种选项。串行化引擎的优点是它隐藏了所有的通常在编写自定义处理程序中需要的下层XML API代码。但是,尽管大多数开发者发现这是有正面意义的,有一些开发者认为它是一个缺陷,因为它们仍然希望在WebMethod实现中手动包装SOAP消息。要了解如何实现这种混合的途径的详细信息,请参阅Accessing Raw SOAP Messages in Web Services。
自动生成WSDL
一旦你已经编写并配置了一个WebMethod,客户端为了成功地与它通讯,需要了解正确地SOAP消息是什么样子的。提供Web服务描述的标准的途径是WSDL(或嵌入式XSD定义)。为了帮助适应这种情况,.asmx处理程序自动生成人类可以阅读的文档页面和准确反映WebMethod接口的WSDL定义。如果你给WebMethods应用了一系列的映射标志,它们都会反映在生成的文档中。
如果你浏览.asmx文件,你会看到类似图2所示的可以阅读的文档页面。该文档页面是由一个叫作DefaultWsdlHelpGenerator.aspx的.aspx页面(位置在C:\windows\\Framework\ v1.0.3705\config)生成的。如果打开这个文件,你可以发现这仅仅是一个使用.NET反射生成文档的标准的页面。这个特性使你的文档一直与代码同步。你可以简单地修改这个文件来自定义生成的文档。
你可以通过在Web.config文件中指定一个不同的文档文件来绕过在虚拟目录基础上的文档生成:
<configuration>
<system.web>
<webServices>
<wsdlHelpGenerator href="MyDocumentation.aspx"/>
</webServices>
...
如果客户端提出的.asmx端点的GET请求在请求字符串中有"?wsdl",那么.asmx处理程序会生成WSDL定义来代替可以阅读的文档。客户端可以使用这个WSDL定义来生成自动了解如何与Web服务通讯的代理类(例如在.NET中使用Wsdl.exe)。
为了自定义WSDL生成进程,你可以编写一个SoapExtensionReflector类并在Web.config文件中向WebMethods框架组件注册。接着,当.asmx处理程序生成WSDL定义时,它将调用你的反射类,给了你自定义最终提供给客户端的定义的机会。如果你要了解更多的编写SoapExtensionReflector类的方法,请查看SoapExtensionReflectors in Web Services。
你可以使用两种不同的技术来绕过WSDL生成进程。第一种是你可以在虚拟目录种为客户端的访问提供静态的WSDL文档并在Web.config文件中删除文档生成部分,如下所示:
<configuration>
<system.web>
<webServices>
<protocols>
<remove name="Documentation"/>
</protocols>
...
另一种稍微自动化的技术是使用[WebServicesBinding]标志来指定实现WebMethod类的虚拟目录中的静态WSDL文档的位置。你也必须使用[SoapDocumentMethod]标志指定帮定到每个WebMethod实现的WSDL的名称。有了这些后,自动化的WSDL生成进程将导入你的静态WSDL文件并在它周围包装一个新的服务描述。如果你要了解这种技术的更多信息,请查阅Place XML Message Design Ahead of Schema Planning to Improve Web Service Interoperability。
目前WSDL极难手动编写,因为没有很多的可用的WSDL编辑器。因此,自动的文档/WSDL生成是WebMethods框架组件中有价值的一部分,很多开发者将很长时间依赖它。
结论
的WebMethods框架组件提供了一条高效率建立Web服务的途径。WebMethods把传统.NET方法为支持HTTP、XML、XML Schema、SOAP和WSDL暴露为Web服务操作成为可能。WebMethods(.asmx)处理程序自动找出怎样把输入的SOAP消息分派给适当的方法,这时它自动把输入的XML元素串行化成相应的.NET对象。为了简化与客户端的集成,.asmx处理程序也提供了生成人类可读(HTML)和计算机可读(WSDL)文档的自动支持。
尽管WebMethods框架组件与自定义IHttpHandlers相比稍微有点限制,但是它也提供了强大的可扩展性模型,就是我们所知道的SOAP扩展框架组件。SOAP扩展允许你根据需要引入附加的功能。例如,微软为.NET发布了Web Services Enhancements 1.0(WSE),它仅仅提供了一个SoapExtension类,该类为WebMethods框架组件引入了对几种GXA规格的支持。如果你需要了解编写SOAP扩展的更多信息,请参阅Fun with SOAP Extensions。