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;
}
33 comments:
Your code was very helpful for me to develop a code in asp.net1.0 and vb.net. Thanks a lot.
Hi: I have a requirement for getting all method names give the name of a webservice. So i would like to use the "www........asmx?wsdl" to query the wsdl and parse the resultant XML to get all method sigantures. Is this what this piece of code does? thanks for the inputs.
(anything between angle brackets -- such as types for generics, comments -- disappears when code is posted this way)
Hello Mike, Can you please provide me code as zipped file for the example given here. Since you have posted type declarations along with HTML it has gone inaccessible.
thanks in advance.
Raj
Mike,
Great job!
Can you please send me thid code too?
ewolthaus at gmail dot com
Thanks!
Thanks for interesting article.
To see the type declarations select 'View Source' from your context menu. The actual code is in there.
Thanks for your comments guys. I very glad that you found this post useful. Sorry about the angle brackets, it's not the first time I've posted code without checking for that :P
Hi Mike
your code is very usefull to me .
i also want to know that how can we send parameters to web method through HttpWebRequest and access response through HttpWebResponse
can you plz send me this code in zip file
Thanks
Hello Mike
I have one query regarding with web service API call.
I have third party Web Service of PHP. i am calling this php web service in asp.net
i have added web reference . Web Method returns ComplexType. i.e it returns Class object of array type.
it execute call successfully. maintain data in log file . but object returns null value.
that means deserilization of xml to object not working properly.
php web service support document/literal format.
can you plz help me to solve this issue.
from last 5 days i m struggling for this
Thanks
Hi I am Darshan,
i have a query..
does any body knows what
htpp://localhost/XYZ/abc.asmx?wsdl=0
i.e. wsdl=0 means...
Thanks in advance
Hi Mike,
I want to extend your code to parse SOAP headers. Any ideas how to do it?
thanks in advance,
George
Hi George,
This code is no good for that. It's specialised for parsing WSDL files.
Mike
Hello Mike,
Can you please provide the code in a zipped file or even email it to kmadhu84@yahoo.co.in
Thanks in advance.
Madhu,
My current practice to create a test solution of any code I publish on my blog so that you can download it and get it working straight away.
Unfortunately this post is nearly three years old and the code has gone to the great repository in the sky (I've lost it).
I don't really have the time or inclination to re-visit this right now, but there should be enough there for you to work it out for yourself.
Sorry for the unhelpful reply.
Mike, thanks for that quick reply.
Though i'm new to the development stream, with the code provided in this blog (and peers help if required) i should come with an outcome.
Thanks once again.
Hi Mike,
It's realy good code, but you code can not return object parameter.
i.e.
HelloWorld1 "Testing"
check int /check
myString
userName string /userName
password string /password
/myString
/HelloWorld1
HelloWorld1Response "Testing"
HelloWorld1Result string /HelloWorld1Result
/HelloWorld1Response
Output:
Web Method:HelloWorld1
Input Parameters:
Check int
myString Certificate
Out Parameters:
HelloWorld1Result string
Is there any way to get objet information like,
Input Parameters:
Check int
myString Certificate
userName string
password string
Please send an zip code on mail account @ abhijit.more11@gmail.com
Thanks in Advance
Hi Abhijit,
WSDL decribes complex types as XSD schemas. You can retrieve the XSD and use a tool such as XSD.exe to generate types from it, that's how the add-web-reference tool in VS works internally.
If you want to be able to generate a message automatically, you simply have to parse the XSD yourself. There are plenty of good libraries out there for doing that.
Sorry I don't have the time to show you code.
Good luck.
Hello Mike,
It's a very useful code. However, this code doesn't retrieve the elements contained within elements. Here is WSDL I tried :
http://ws.strikeiron.com/ReversePhoneLookup?WSDL
I am interested in these values : -
name="ListingName"
name="FirstName" name="Address" etc.
Do you know a way to retrieve this values?
Thanks in advance.
Hi Anonymous,
No, you are right, this code simply demonstrates passing simple values as parameters. It is far from a complete solution. Check out my comment to Abhijit above for some pointers about how to work with complex types.
Mike
thanks Mike
Mike,
How to get the complex class details (i.e. for the class which falls as input / output to any web-method), the details I am looking for are :
1. Class attributes which are useful in xml-serialization
2. How to perform nesting on complex types to retrieve the info. same as point 1
> vaibhav.gaikwad@gmail.com
Hi Mike,
It does not work for the WSDL hosted below...
http://api.clickatell.com/soap/webservice.php?WSDL
Note sure how you stubled upon this solution to the problem.
It most of taken a good amount of effort to work tis one out.
Either way thanks for the help!
akpc,
Wasn't too bad. Reflector was the secret weapon :)
Hi,
Reading WSDL from both webservice and WCF.
http://hussainahamed.blogspot.com/2010/10/reading-wsdl-from-both-webservice-and.html
Hi,
Thanks for this superb post.I have one query,
What would be the modification if we have wsdl with authentication required.
This is a great post.. Thanks for sharing!
Hi guys this is a great source for getting types but i was wondering code Rant has specified we can achieve this using service description classes do any one have any sources how we can do that.
Thanks man...You are the best
usefull
Great post! Thank you very much.
It would take anyone unfamiliar wiht ServiceDescription one or two days to figure out how to drill down ServiceDescription to get all the parameter information.
The code has some case issues (primarily some lowercase initials should be uppercase), but they can be found easily when it is compiled.
It fails to catch array parameters. It should have something like the following:
if(childElement.MaxOccurs > 1)
{
parameters.Add(new Parameter(parameterName, parameterType + "[]"));
}
else
{
parameters.Add(new Parameter(parameterName, parameterType));
}
Post a Comment