改进 ASP.NET WebService 帮助生成器以反映继承






3.63/5 (5投票s)
将 XSL 转换应用于 ASP.NET WebService 的服务描述,以生成更好的接口文档。
引言
XML WebService 可以在 .NET Framework 中非常容易地开发,而且它还为我们提供了一个很好的帮助生成器,因此您可以自动向您的客户展示他们如何与您的服务进行通信。
不幸的是,生成的帮助没有反映继承的数据结构,因此一旦使用这些结构来构建接口,它就会变得毫无用处。当创作 XML WebService 时,继承当然是一个非常有用的技术。例如,您可能需要每个请求的登录数据,或者您可能希望每个响应都回复一些状态数据。因此,每个请求结构可能继承一个通用结构,每个响应可能继承一个通用响应结构。
背景
XML WebService 及其使用者之间的数据交换由 WSDL 契约定义。.NET Framework 会根据 WebService 作者定义的结构自动创建它。这样的契约在外部由 XML Schema 文档表示,在内部由 System.Web.Services.Description.ServiceDescription
类的实例表示。在任何一种情况下,WSDL 都使用指向基结构的 XML Schema 扩展元素准确地反映任何继承结构。但是,默认的帮助呈现算法不遵循这些扩展引用,因此基结构仍然不可见。
解决方案
您可以编写自己的帮助生成器,以便生成您喜欢的任何类型的文档页面。WsdlHelpGenerator
的位置可以在 *web.config* 中定义。
但我真的不想重写或更改默认的渲染算法,所以我的解决方案是稍微修改 WSDL 数据,使用一个简单的 XSL 转换,用适当的基结构替换任何扩展引用。这可以通过处理 WsdlHelpGenerator
的 PreLoad
事件来完成,因此除了向 WsdlHelpGenerator
类添加两个方法之外,不需要更改任何代码。
解决问题的三个步骤
-
获取已安装的默认描述生成器 *DefaultWsdlHelpGenerator.aspx*(在我的计算机上,它位于 *C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG* 中),并将其另存为 WebService 的 Web 目录中的 *WsdlHelpGenerator.aspx*。打开您的 *web.config* 并放入...
<webServices> <wsdlHelpGenerator href="WsdlHelpGenerator.aspx" /> </webServices>
... 在
<system.web>
部分中。 -
打开 *
WsdlHelpGenerator
.aspx* 并在Page_Load
方法的正下方添加这两个方法protected override void OnPreLoad(EventArgs e) { base.OnPreLoad(e); // transform any service description stored within HttpContext // cf. Page_Load: try "wsdlsWithPost" first and fall back to "wsdls" string key = Context.Items["wsdlsWithPost"] != null ? "wsdlsWithPost" : "wsdls"; serviceDescriptions = (ServiceDescriptionCollection)Context.Items[key]; TransformServiceDescriptions(ref serviceDescriptions); Context.Items[key] = serviceDescriptions; } void TransformServiceDescriptions(ref ServiceDescriptionCollection descriptions) { // modify each description by an XSLT processor ServiceDescriptionCollection transformed = new ServiceDescriptionCollection(); System.Xml.Xsl.XslCompiledTransform xslt = new System.Xml.Xsl.XslCompiledTransform(); xslt.Load(Server.MapPath("WsdlHelp.xsl")); foreach (ServiceDescription desc in descriptions) { // load original WSDL data MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream(); desc.Write(ms1); // process WSDL data using WsdlHelp.xsl ms1.Position = 0; xslt.Transform(new System.Xml.XPath.XPathDocument(ms1), null, ms2); // replace current WSDL data with the transformed stream ms2.Position = 0; transformed.Add(ServiceDescription.Read(ms2)); ms1.Dispose(); ms2.Dispose(); } descriptions = transformed; }
-
最后,为了使此代码正常工作,请将转换文件 *WsdlHelp.xsl* 放入 WebService 的 Web 目录中。它可能如下所示
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl=http://www.w3.org/1999/XSL/Transform xmlns:s="http://www.w3.org/2001/XMLSchema"> <xsl:output method="xml" indent="no" encoding="utf-8" omit-xml-declaration="no" /> <!-- recursively dissolve any schema extension elements to the base structure --> <xsl:template match="/" xml:space="default"> <xsl:apply-templates /> </xsl:template> <xsl:template match="*" priority="0.5" xml:space="default"> <xsl:copy> <xsl:copy-of select="attribute::*" /> <xsl:choose> <xsl:when test="child::*" /> <xsl:otherwise> <xsl:value-of select="." /> </xsl:otherwise> </xsl:choose> <xsl:apply-templates select="child::*" /> </xsl:copy> </xsl:template> <xsl:template match="s:complexType" priority="1.0"> <xsl:element name="s:complexType" namespace="http://www.w3.org/2001/XMLSchema"> <xsl:copy-of select="attribute::*" /> <xsl:element name="s:sequence"> <xsl:copy-of select=".//s:sequence/*" /> <xsl:if test="./s:complexContent/s:extension"> <xsl:comment> schema extension expanded: <xsl:value-of select="./s:complexContent/s:extension/@base"/> </xsl:comment> <xsl:call-template name="fetch-sequence"> <xsl:with-param name="typename" select="substring-after(./s:complexContent/s:extension/@base,':')" /> </xsl:call-template> </xsl:if> </xsl:element> </xsl:element> </xsl:template> <xsl:template name="fetch-sequence"> <xsl:param name="typename" /> <xsl:copy-of select="//s:complexType[@name = $typename]//s:sequence/*" /> <xsl:if test="//s:complexType[@name = $typename]/s:complexContent/s:extension"> <xsl:call-template name="fetch-sequence"> <xsl:with-param name="typename" select="substring-after(//s:complexType[@name = $typename] /s:complexContent/s:extension/@base,':')" /> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet>
关注点
一旦您有了自己的 WsdlHelpGenerator
,您也可以轻松地修改外观,添加您自己的样式表、公司徽标等。
如果您不希望您的 WebService 方法列表按字母顺序排序,请将 WsdlHelpGenerator
的 Page_Load
方法中的 SortedList methodsTable
替换为未排序的列表。
您也可以查看优秀的文章 外部化 Web 服务文档 并在那里应用 XSL 转换。只需将上面的 C# 代码放入 *ServiceDescriptionGenerator.aspx.cs* 文件中即可。
历史
- 2008 年 8 月 17 日:初始帖子