本篇文章为大家展示了如何理解.asmx处理程序提供的XML映射功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
将 XML映射到对象
在 WebMehod 处理程序确定了要调用的方法之后,它需要将 XML 消息反序列化为可在方法调用过程中提供的 .NET 对象。如同消息调度一样,该处理程序通过以下方法来实现上述目标:通过反射来检查该类,以便确定如何处理传入的 XML 消息。XmlSerializer 类在 System.Xml.Serialization 命名空间中自动完成 XML 和对象之间的映射。
XmlSerializer 使将任何公共的 .NET 类型映射到 XML 架构类型成为可能,在建立了这样的映射之后,它可以在 .NET 对象和 XML 实例文档之间自动映射(请参阅图 4)。目前,XmlSerializer 被限制于 XML 架构所支持的模型中,因此无法处理当今所有复杂的现代对象模型,例如,复杂的非树型对象图、双重指针等。不过,XmlSerializer 能够处理开发人员倾向使用的大多数复杂类型。
对于上面说明的 Add 示例,XmlSerializer 会将 x 和 y 元素映射为 .NET 双精度值,这些值随后会在调用 Add 时提供。Add 方法向调用方返回一个双精度值,该值随后将需要重新序列化为 SOAP 响应中的一个 XML 元素。
图 4. 将 XML映射到对象
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.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 元素,该元素包含一个双精度类型的 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)); } }
这个版本的 Distance 希望传入具有如下外观的 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 响应消息:
< 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 定义将包含用来描述 SOAP 消息中所使用的请求和响应元素的字面上的 XML 架构定义(例如,不使用 SOAP 编码规则)。
.asmx 处理程序还可以使用 SOAP rpc/encoded 样式。这意味着 SOAP 正文中包含一个 RPC 调用的 XML 表示形式,而且参数都使用 SOAP 编码规则(例如,不需要 XML 架构)进行了序列化。为了实现这个目标,可以使用 [SoapRpcService] 和 [SoapRpcMethod] 属性,而不使用 [SoapDocumentService] 和 [SoapDocumentMethod] 属性。有关这些样式之间的区别的更多信息,请查看 Understanding SOAP。
正如您所看到的一样,可以完全自定义给定方法映射到 SOAP 消息的方式。XmlSerializer 提供一个功能强大的序列化引擎,以及许多我们在本文中没有时间进行讨论的功能。有关 XmlSerializer 如何工作的更多信息,请查看 Moving to .NET and Web Services。在我的每月 MSDN Magazine 的 XML Files 专栏(可在联机存档中查看专栏列表)中,我还介绍了 XmlSerializer 的许多不易察觉的细微差别。
除了对参数的反序列化进行处理以外,.asmx 处理程序还能够对 SOAP 头进行反序列化/序列化。SOAP 头的处理方法与参数不同,因为它们通常被视为带外信息,并未直接关联到某个特定的方法。因此,SOAP 头的处理通常是通过侦听层完成的,从而使得 WebMethod 完全无须对 SOAP 头进行处理。
但是,如果您希望亲自处理 WebMethod 中的头信息,则必须提供一个从 SoapHeader 派生的 .NET 类,此类代表该头的 XML 架构类型(遵循上面描述的同一映射准则)。然后定义该类型的成员变量,以便让其充当头实例的占位符。***,批注每个需要访问该头的 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"> ...
为了使 .asmx 处理程序能够反序列化该头,首先需要定义一个表示隐含的 XML 架构类型的 .NET 类(注:如果您实际上已经知道了该头的 XML 架构,则可以使用 xsd.exe /c 来生成该类)。在本例中,相应类的外观如下所示:
[XmlType(Namespace="http://example.org/security")] [XmlRoot(Namespace="http://example.org/security")] 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.xmlsoap.org/soap/envelope/" > < soap:Body> < soap:Fault> < faultcode>soap:Server< /faultcode> < faultstring>Server was unable to process request. --> 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 消息。
上述内容就是如何理解.asmx处理程序提供的XML映射功能,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。