Click here to Skip to main content
6,595,854 members and growing! (17,682 online)
Email Password   helpLost your password?
Languages » XML » Web Services     Intermediate License: The Code Project Open License (CPOL)

Improving the ASP.NET Webservice Help Generator to Reflect Inheritance

By Sebastian Böthin

Apply an XSL transformation to the service description of an ASP.NET webservice to generate better interface documentation.
C#, XML, XSLT, Windows, .NET, ASP.NET, Architect, Dev
Version:2 (See All)
Posted:17 Aug 2008
Views:11,092
Bookmarked:10 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
5 votes for this article.
Popularity: 2.53 Rating: 3.63 out of 5
1 vote, 20.0%
1

2

3
2 votes, 40.0%
4
2 votes, 40.0%
5

Introduction

XML webservices can be developed very easily within the .NET Framework, and moreover it provides us with a nice help generator so you can automatically show your clients how they have to talk with your service.

Unfortunately, the generated help doesn't reflect inherited data structures, so it becomes quite useless as soon as such structures are being used to build the interface. Inheritance is of course a very useful technique when authoring XML webservices. So, for instance, you may require login data for each request or you may want to reply some status data with each response. Thus every request structure may inherit a generic one and every response may inherit a generic response structure.

Background

Data exchange between an XML webservice and its consumers is defined by a WSDL contract. The .NET Framework creates it automatically from the structures defined by the webservice author. Such a contract is represented externally by an XML Schema document and internally by an instance of the System.Web.Services.Description.ServiceDescription class. In either case, the WSDL reflects exactly any inheritance structure using the XML Schema extension element pointing to the base structure. However, the default help rendering algorithm doesn't follow these extension references, therefore base structures remain unseen.

Solution

You can write your own help generator in order to generate any kind of documentation pages you like. The location of the WsdlHelpGenerator can be defined in the web.config.

But I really don't want to rewrite or change the default rendering algorithm, so my solution is to modify the WSDL data slightly for it, using a simple XSL transformation that replaces any extension reference by the appropriate base structure. This can be done by handling the PreLoad event of the WsdlHelpGenerator, so no code needs to be changed at all, apart from adding two methods to the WsdlHelpGenerator class.

Three Steps to Solve the Problem

  1. Get the installed default description generator DefaultWsdlHelpGenerator.aspx (on my computer, it's in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG) and save it as WsdlHelpGenerator.aspx in the web directory of your webservice. Open your web.config and put...

      <webServices>
        <wsdlHelpGenerator href="WsdlHelpGenerator.aspx" />
      </webServices>

    ... inside the <system.web> section.

  2. Open WsdlHelpGenerator.aspx and add these two methods directly below the Page_Load method:

     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. Finally, to get this code working, put the transformation file WsdlHelp.xsl into the web directory of your webservice. It may look as follows:

    <?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>

Points of Interest

Once you have your own WsdlHelpGenerator, you can also easily modify the appearance, add your own stylesheets, your company logo, etc.

If you don't want to have your webservice method list sorted alphabetically, replace the SortedList methodsTable within the Page_Load method of the WsdlHelpGenerator by an unsorted list.

You may also have a look at the excellent article Externalizing Web Service Documentation and apply XSL transformation there. Just put the C# code above into the ServiceDescriptionGenerator.aspx.cs file.

History

  • 17th August, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sebastian Böthin


Member
I studied math and computer science in Berlin, focused on Algebraic Geometry. Experience in software development since 1999. I'm doing .NET during the day, but things like Perl and other stuff at night Wink
Occupation: Software Developer
Location: Germany Germany

Other popular XML articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 17 Aug 2008
Editor: Deeksha Shenoy
Copyright 2008 by Sebastian Böthin
Everything else Copyright © CodeProject, 1999-2009
Web18 | Advertise on the Code Project