Wsdl is great, it's like reflection for web services. As I discussed in
this post, you can use it to dynamically discover and call web services at runtime. The dot net framework provides a class, 'System.Web.Services.Description.ServiceDescription' that takes a wsdl stream and provides an object model to walk the wsdl. It's really powerfull, but kinda awkward to use if you just want to get a list of web methods with their parameter names and types. Faced with this requirement last week, I put together a simple facade for the ServiceDescription class. You just create a new WebServiceInfo instance using its factory method 'OpenWsdl' and then you can iterate through all the web methods and their parameters and return parameters. Here's the NUnit test which pretty much shows how it works:
[Test]
public void ServiceDescriptionSpike()
{
string url = "http://localhost:1297/Test/TestService.asmx";
WebServiceInfo webServiceInfo = WebServiceInfo.OpenWsdl(new Uri(url));
Console.WriteLine(string.Format("WebService: {0}", webServiceInfo.Url));
foreach (WebMethodInfo method in webServiceInfo.WebMethods)
{
Console.WriteLine(string.Format("\tWebMethod: {0}", method.Name));
Console.WriteLine("\t\tInput Parameters");
foreach (Parameter parameter in method.InputParameters)
{
Console.WriteLine(
string.Format("\t\t\t{0} {1}", parameter.Name, parameter.Type));
}
Console.WriteLine("\t\tOutput Parameters");
foreach (Parameter parameter in method.OutputParameters)
{
Console.WriteLine(
string.Format("\t\t\t{0} {1}", parameter.Name, parameter.Type));
}
}
}
And here's the code. As you can see it just wraps the ServiceDescription class, you can see what a pain it is to get this information out of it.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Web.Services.Description;
using System.Net;
/// <summary>
/// Information about a web service
/// </summary>
public class WebServiceInfo
{
WebMethodInfoCollection _webMethods = new WebMethodInfoCollection();
Uri _url;
static Dictionary<string, webserviceinfo=""> _webServiceInfos =
new Dictionary<string, webserviceinfo="">();
/// <summary>
/// Constructor creates the web service info from the given url.
/// </summary>
/// <param name="url">
private WebServiceInfo(Uri url)
{
if (url == null)
throw new ArgumentNullException("url");
_url = url;
_webMethods = GetWebServiceDescription(url);
}
/// <summary>
/// Factory method for WebServiceInfo. Maintains a hashtable WebServiceInfo objects
/// keyed by url in order to cache previously accessed wsdl files.
/// </summary>
/// <param name="url">
/// <returns></returns>
public static WebServiceInfo OpenWsdl(Uri url)
{
WebServiceInfo webServiceInfo;
if (!_webServiceInfos.TryGetValue(url.ToString(), out webServiceInfo))
{
webServiceInfo = new WebServiceInfo(url);
_webServiceInfos.Add(url.ToString(), webServiceInfo);
}
return webServiceInfo;
}
/// <summary>
/// Convenience overload that takes a string url
/// </summary>
/// <param name="url">
/// <returns></returns>
public static WebServiceInfo OpenWsdl(string url)
{
Uri uri = new Uri(url);
return OpenWsdl(uri);
}
/// <summary>
/// Load the WSDL file from the given url.
/// Use the ServiceDescription class to walk the wsdl and create the WebServiceInfo
/// instance.
/// </summary>
/// <param name="url">
private WebMethodInfoCollection GetWebServiceDescription(Uri url)
{
UriBuilder uriBuilder = new UriBuilder(url);
uriBuilder.Query = "WSDL";
WebMethodInfoCollection webMethodInfos = new WebMethodInfoCollection();
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Method = "GET";
webRequest.Accept = "text/xml";
ServiceDescription serviceDescription;
using (System.Net.WebResponse response = webRequest.GetResponse())
using (System.IO.Stream stream = response.GetResponseStream())
{
serviceDescription = ServiceDescription.Read(stream);
}
foreach (PortType portType in serviceDescription.PortTypes)
{
foreach (Operation operation in portType.Operations)
{
string operationName = operation.Name;
string inputMessageName = operation.Messages.Input.Message.Name;
string outputMessageName = operation.Messages.Output.Message.Name;
// get the message part
string inputMessagePartName =
serviceDescription.Messages[inputMessageName].Parts[0].Element.Name;
string outputMessagePartName =
serviceDescription.Messages[outputMessageName].Parts[0].Element.Name;
// get the parameter name and type
Parameter[] inputParameters = GetParameters(serviceDescription, inputMessagePartName);
Parameter[] outputParameters = GetParameters(serviceDescription, outputMessagePartName);
WebMethodInfo webMethodInfo = new WebMethodInfo(
operation.Name, inputParameters, outputParameters);
webMethodInfos.Add(webMethodInfo);
}
}
return webMethodInfos;
}
/// <summary>
/// Walk the schema definition to find the parameters of the given message.
/// </summary>
/// <param name="serviceDescription">
/// <param name="messagePartName">
/// <returns></returns>
private static Parameter[] GetParameters(ServiceDescription serviceDescription, string messagePartName)
{
List<parameter> parameters = new List<parameter>();
Types types = serviceDescription.Types;
System.Xml.Schema.XmlSchema xmlSchema = types.Schemas[0];
foreach (object item in xmlSchema.Items)
{
System.Xml.Schema.XmlSchemaElement schemaElement = item as System.Xml.Schema.XmlSchemaElement;
if (schemaElement != null)
{
if (schemaElement.Name == messagePartName)
{
System.Xml.Schema.XmlSchemaType schemaType = schemaElement.SchemaType;
System.Xml.Schema.XmlSchemaComplexType complexType = schemaType as System.Xml.Schema.XmlSchemaComplexType;
if (complexType != null)
{
System.Xml.Schema.XmlSchemaParticle particle = complexType.Particle;
System.Xml.Schema.XmlSchemaSequence sequence = particle as System.Xml.Schema.XmlSchemaSequence;
if (sequence != null)
{
foreach (System.Xml.Schema.XmlSchemaElement childElement in sequence.Items)
{
string parameterName = childElement.Name;
string parameterType = childElement.SchemaTypeName.Name;
parameters.Add(new Parameter(parameterName, parameterType));
}
}
}
}
}
}
return parameters.ToArray();
}
/// <summary>
/// WebMethodInfo
/// </summary>
public WebMethodInfoCollection WebMethods
{
get { return _webMethods; }
}
/// <summary>
/// Url
/// </summary>
public Uri Url
{
get { return _url; }
set { _url = value; }
}
}
/// <summary>
/// Information about a web service operation
/// </summary>
public class WebMethodInfo
{
string _name;
Parameter[] _inputParameters;
Parameter[] _outputParameters;
/// <summary>
/// OperationInfo
/// </summary>
public WebMethodInfo(string name, Parameter[] inputParameters, Parameter[] outputParameters)
{
_name = name;
_inputParameters = inputParameters;
_outputParameters = outputParameters;
}
/// <summary>
/// Name
/// </summary>
public string Name
{
get { return _name; }
}
/// <summary>
/// InputParameters
/// </summary>
public Parameter[] InputParameters
{
get { return _inputParameters; }
}
/// <summary>
/// OutputParameters
/// </summary>
public Parameter[] OutputParameters
{
get { return _outputParameters; }
}
}
/// <summary>
/// A collection of WebMethodInfo objects
/// </summary>
public class WebMethodInfoCollection : KeyedCollection<string, webmethodinfo="">
{
/// <summary>
/// Constructor
/// </summary>
public WebMethodInfoCollection() : base() { }
protected override string GetKeyForItem(WebMethodInfo webMethodInfo)
{
return webMethodInfo.Name;
}
}
/// <summary>
/// represents a parameter (input or output) of a web method.
/// </summary>
public struct Parameter
{
/// <summary>
/// constructor
/// </summary>
/// <param name="name">
/// <param name="type">
public Parameter(string name, string type)
{
this.Name = name;
this.Type = type;
}
/// <summary>
/// Name
/// </summary>
public string Name;
/// <summary>
/// Type
/// </summary>
public string Type;
}