|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionXML webservices can be devolped 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 a XML webservice and it's 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 a XML Schema document
and internally by an instance
of the Solution
You can write your own help generator in order to generate any kind of
documentation pages you like. The location of the
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 Three steps to solve the problem
1) Get the installed default description generator
<webServices>
<wsdlHelpGenerator href="WsdlHelpGenerator.aspx" />
</webServices>
inside the 2) Open 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 you 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
If you don't want to have your webservice method list sorted alphabetically,
replace the SortedList
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
|
||||||||||||||||||||||