65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.63/5 (5投票s)

2008 年 8 月 17 日

CPOL

2分钟阅读

viewsIcon

48099

downloadIcon

463

将 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 转换,用适当的基结构替换任何扩展引用。这可以通过处理 WsdlHelpGeneratorPreLoad 事件来完成,因此除了向 WsdlHelpGenerator 类添加两个方法之外,不需要更改任何代码。

解决问题的三个步骤

  1. 获取已安装的默认描述生成器 *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> 部分中。

  2. 打开 *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;
     }
  3. 最后,为了使此代码正常工作,请将转换文件 *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 方法列表按字母顺序排序,请将 WsdlHelpGeneratorPage_Load 方法中的 SortedList methodsTable 替换为未排序的列表。

您也可以查看优秀的文章 外部化 Web 服务文档 并在那里应用 XSL 转换。只需将上面的 C# 代码放入 *ServiceDescriptionGenerator.aspx.cs* 文件中即可。

历史

  • 2008 年 8 月 17 日:初始帖子
改进 ASP.NET WebService 帮助生成器以反映继承 - CodeProject - 代码之家
© . All rights reserved.