Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | Videos | Photos | Downloads | Blogs | E-Books | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » Web Services » Generic Web Service Proxy

Generic Web Service Proxy

In this article I will explain how you can consume a web service without using the standard "Add Web Reference" in Visual Studio and how to do this without using SOAP.

Technologies: .NET 1.0/1.1, Web Services,Visual C# .NET
Total downloads :
Total page views :  49730
Rating :
 4/5
This article has been rated :  1 times
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
 
Become a Sponsor


Related EbooksTop Videos

Introduction

In this article I will explain how you can consume a web service without using the standard "Add Web Reference" in Visual Studio and how to do this without using SOAP.

"Why do this?" you might ask, well, there are several reasons, for example, you could be discovering the actual web service you wish to consume dynamically and the actual Web Services may vary dramatically, depending on how dynamic you have to be static references to every possible Web Service may not be practical/possible. My reason for doing this: "Out of interest!"

HTTP Background

Firstly, a little background on HTTP or Hyper Text Transfer Protocol, if you've never considered looking in depth at this protocol, that we all so readily use and take for granted, you may be thinking "Oh! No! Too advanced", but you'd be wrong!

HTTP is an incredibly simple protocol, an important thing to note is that HTTP 1.0 is not connection based, a request for a web page or resource from a server is made the response received and then the connection is closed. This poses problems for Web Application creators as there is no innate state in HTTP 1.0 except without the use of things like Cookies. HTTP 1.1 is by default connection based, yet everything still appears to behave like HTTP 1.0.

All HTTP is, is a protocol to send a request and to receive a response. Each request will consist of several headers, an example of which is "Set-Cookie" and another example could be "Content-Length". The former is, essentially, going to have a list of name/value pairs which represent the actual cookies, there is other information such as path, domain and expiry that can be associated with an individual cookie. Requests often contain content too, the header Content-Length is used to explicitly state the length of the content (how surprising!). A response is equally simple and also consists of headers and content an example of the content could be HTML.

I would go into more detail, but I won't as its not the point of this article!

Creating our Generic Web Service Proxy

Note: The namespaces I will use will be:

  • System.Text
  • System.Text.RegularExpressions
  • System.Xml
  • System.Net

(you can either fully qualify the classes I use or add the using statements to the top of your code)

Making a HTTP Request and Receiving a HTTP Response

To make a HTTP Request, firstly you need to create an instance of the class HttpWebRequest, to do this you can do the following:

string myURL = http://www.cratos-development.co.uk;
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(myURL);

Why am I casting the return value of .Create(...)? Well, the .Create() method returns a WebRequest not a HttpWebRequest.

The next step is to set up any header information, there is a header collection you can use, but the most common headers have strongly-typed properties sitting on the HttpWebRequest object. The you should set up any content that is required (I will come to this in the last section).

To receive a response, synchronously, from the web server all you need do is the following:

HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

Again, the result of this method is WebResponse and not HttpWebResponse.

This is all done synchronously and by that I mean, when the program reaches the line above, it will not continue executing until a response has been fully received. Although in my Generic Web Service Proxy I have only used the synchronous call, for simplicities sake on the client of this class, in the next section I will briefly cover how to make an Asynchronous call.

Receiving a HTTP Response Asynchronously

To make an Asynchronous request, this is a little more complex, firstly we need to create a method to handle the response when it comes, therefore, your HttpWebRequest object needs to have a larger scope than just one method. Below is a code snippet from the method that will start but not finish the request:

// .. CODE HERE ..
IAsyncResult = this._httpRequest.BeginGetResponse(
new AsyncCallback(this.ResponseReceivedHandler),
null);
// .. CODE HERE ..

AsyncCallback is a delegate that gets fired by the .Net Framework when the full response is received. this.ResponseReceivedHandler is a method that is subscribed to that delegate and therefore, when AsyncCallback is invoked, so too will ResponseReceivedHandler be invoked.

Below is the method (ResponseReceivedHandler) which will get the final response:

private void ResponseReceivedHandler(IAsyncResult result)
{
HttpWebRequest httpRequest =
this._httpRequest.EndGetResponse(result);
// .. CODE HERE ..
}

Obtaining Web Service Details

To obtain information about what is needed to call a web method, we can request the web services WSDL, this is obtained via calling the Web Service and passing WSDL as a parameter without a value to the Web Service e.g. http://www.cratos-development.co.uk/cratosdevelopmenttools.asmx?WSDL

This will return an XSD describing the Web Service and all its Web Methods. The following code will obtain the WSDL:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(this._webServiceURI + "?WSDL");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
System.IO.Stream baseStream = response.GetResponseStream();
System.IO.StreamReader responseStreamReader =
new System.IO.StreamReader(baseStream);
string wsdl = responseStreamReader.ReadToEnd();
responseStreamReader.Close();

Calling Web Service Using POST

When making the request to the web service, we need to pass the required parameters, this is done, when using POST without SOAP, by passing to the Web Service a set of name value pairs that look a lot like:

Parameter1=Value1&Parameter2=Value2&Parameter3=Value3

The Generic Web Service Proxy class will expose a method called CallWebMethod, it will take an array of objects ie:

public string CallWebMethod(params object[] parameters)

(this is returning a string for simplicities sake, you could deserialise the result to create an object, but im leaving that up to the client. This is partly due to the fact that many of the Web Methods I work with return Xml (this isn't so good, I know!) )

Using the WSDL and the array of values we will build up a string like the above to place into the content of our HttpWebRequest.

To do this, we will build a string that looks like:

Parameter1=[0]&Parameter2=[1]&Parameter3=[2]

The following four methods obtain the WSDL information and build the above string:

private string GetRequestFormat()
{
const string XPATH_TO_WEB_METHOD_INFORMATION_NODE = "/types/schema/element[@name=\"{0}\"]/*";
const string XPATH_TO_WEB_METHOD_PARAMETERS = "sequence/element";
string xpathToWebMethodInformationNode = string.Format(
PATH_TO_WEB_METHOD_INFORMATION_NODE,
this._webMethodName);
string wsdl = this.GetWSDLForWebMethod();
System.Xml.XmlDocument wsdlDocument =
new System.Xml.XmlDocument();
wsdlDocument.LoadXml(wsdl);
System.Xml.XmlNode webMethodInformationNode = wsdlDocument.SelectSingleNode(xpathToWebMethodInformationNode);
System.Xml.XmlNodeList parameterInformationNodes = webMethodInformationNode.SelectNodes(XPATH_TO_WEB_METHOD_PARAMETERS);
return this.BuildRequestFormatFromNodeList(parameterInformationNodes);
}
private string BuildRequestFormatFromNodeList(System.Xml.XmlNodeList parameterInformationNodes)
{
const string PARAMETER_NAME_VALUE_PAIR_FORMAT = "{0}=[{1}]";
System.Text.StringBuilder requestFormatToReturn =
new System.Text.StringBuilder();
for(int i = 0; i < parameterInformationNodes.Count; i++)
{
requestFormatToReturn.Append(
string.Format(PARAMETER_NAME_VALUE_PAIR_FORMAT, parameterInformationNodes[i].Attributes["name"].Value,
i) +
((i < parameterInformationNodes.Count - 1 &&
parameterInformationNodes.Count > 1) ? "&" :
string.Empty));
}
return requestFormatToReturn.ToString();
}
private string GetWSDLForWebMethod()
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
this._webServiceURI + "?WSDL");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
System.IO.Stream baseStream = response.GetResponseStream();
System.IO.StreamReader responseStreamReader =
new System.IO.StreamReader(baseStream);
string wsdl = responseStreamReader.ReadToEnd();
responseStreamReader.Close();
return this.ExtractTypesXmlFragment(wsdl);
}
private string ExtractTypesXmlFragment(string wsdl)
{
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_HTTP = "http:";
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_SOAP = "soap:";
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_SOAPENC = "soapenc:";
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_TM = "tm:";
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_S = "s:";
const string CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_MIME = "mime:";
const string CONST_TYPES_REGULAR_EXPRESSION = "<types>[\\s\\n\\r=\"<>a-zA-Z0-9.\\.:/\\w\\d%]+</types>";
System.Collections.ArrayList namespaceDeclarationsToRemove =
new System.Collections.ArrayList();
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_HTTP);
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_MIME);
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_S);
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_SOAP);
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_SOAPENC);
namespaceDeclarationsToRemove.Add( CONST_XML_NAMESPACE_REFERENCE_TO_REMOVE_TM);
for(int i = 0; i < namespaceDeclarationsToRemove.Count; i++)
{
wsdl = wsdl.Replace((
string)namespaceDeclarationsToRemove[i], string.Empty);
}
System.Text.RegularExpressions.Match match =
System.Text.RegularExpressions.Regex.Match(wsdl, CONST_TYPES_REGULAR_EXPRESSION);
return match.Groups[0].Value;


(I apologize for using a regular expression to pull out the data, if there any XPath and XML/XSD experts that would like to tell me how to extract the information I require that would be excellent, but until then, this will have to remain as ugly as it is - also it's a pretty ugly regular expression too!)

Ok, so now we are able to generate a single string, woohooo! But, oddly, that was the most complex part of the whole object, in my opinion, what follows now is the method that actually calls the Web Method:

internal string CallWebMethod(params object[] parameters)
{
byte[] requestData =this.CreateHttpRequestData(parameters);
string uri = this._webServiceURI + "/" + this._webMethodName;
HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
httpRequest.Method = "POST";
httpRequest.KeepAlive =
false;
httpRequest.ContentType = "application/x-www-form-urlencoded";
httpRequest.ContentLength = requestData.Length;
httpRequest.Timeout = 30000;
HttpWebResponse httpResponse =
null;
string response = string.Empty;
try
{
httpRequest.GetRequestStream().Write(requestData, 0, requestData.Length);
httpResponse = (HttpWebResponse)httpRequest.GetResponse();
System.IO.Stream baseStream = httpResponse.GetResponseStream();
System.IO.StreamReader responseStreamReader =
new System.IO.StreamReader(baseStream);
response = responseStreamReader.ReadToEnd();
responseStreamReader.Close();
}
catch(WebException e)
{
const string CONST_ERROR_FORMAT = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Exception><{0}Error>{1}<InnerException>{2}</InnerException></{0}Error></Exception>";
response =
string.Format(CONST_ERROR_FORMAT, this._webMethodName, e.ToString(), (e.InnerException != null ? e.InnerException.ToString() : string.Empty));
}
return response;
}

You may notice that there are some member variables, such as this._webServiceURI, these are assign in the constructor as below:

public WebServiceProxy(string webServiceURI, string webMethodName): base()
{
this._webServiceURI = webServiceURI;
this._webMethodName = webMethodName;
string hashTableKey = this._webServiceURI + "/" + this._webMethodName;
if (_webServiceWebMethodRequestFormats.ContainsKey(hashTableKey))
{
this._requestFormat = (string)_webServiceWebMethodRequestFormats[hashTableKey];
}
else
{
this._requestFormat = this.GetRequestFormat();
_webServiceWebMethodRequestFormats.Add(hashTableKey,
this._requestFormat);
}
}

In my class I have defined a static member, which is a System.Collections.Hashtable and it is called
_webServiceWebMethodRequestFormats, what this does is to ensure that the WSDL request and interpretation to produce the request format (e.g. Parameter1=[0]&Parameter2=[1]) only happens once.

So, there you have it a Generic Web Service Proxy class, without using SOAP! (I have only given you enough information to enable you to create your own class) Please, if anyone can tell me how to extract the information from the WSDL in a more elegant way, I'd love to know (I only have so much time to play about with XSD's and XPath).


Login to add your contents and source code to this article
 [Top] Rate this article
 About the author
 
Chris Blake
Chris Blake started his career in pascal, delphi and C++, working for a UK charity, the RNIB, in early 2002. Quickly he moved to .NET 1.0 Beta and has been with .NET to the present. He currently works for Tesco.com in the United Kingdom and is passionate about SOA, OO-Design, Accessibility and Security.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010
Microsoft Visual Studio 2010 offers more to developers than any other Visual Studio release. Work more productively and collaboratively-with greater control over your work at every step. The Beta 2 can give you a head start on achieving efficiency.
 
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
Internal Server Error when trying myRequest.GetResponse() on RF network by Navraj On March 24, 2007
HI There I have tried to get a response from a URL through the HttpWebRequest class.It woks fine if i deoply the code on emulator and try to access a web service deployed on the same machine through active sync but when i deploy the code on a mobile and access through a RF network it throws Protocol error.Any guesses!
Reply | Email | Delete | Modify | 
CreateHttpRequestData by Cagatay On May 28, 2007
Where is CreateHttpRequestData defined
Reply | Email | Delete | Modify | 
Re: CreateHttpRequestData by Erwin On June 14, 2007

Should be something like this:

 

private byte[] CreateHttpRequestData(object[] parameters)

{

StringBuilder requestStream = new System.Text.StringBuilder();

String p = this._requestFormat;

int l = 0;

foreach (object k in parameters)

{

p = p.Replace("[" + l + "]", k.ToString());

l++;

}

requestStream.Append(p);

UTF8Encoding encoding = new UTF8Encoding();

return encoding.GetBytes(requestStream.ToString());

}

Reply | Email | Delete | Modify | 
.Net 2.0 by Jack On January 8, 2008
Hi There, This doesn't seem to work against web services deployed with 2.0 Can anyone help, please? Thanks, Jack.
Reply | Email | Delete | Modify | 
web service by preeti On February 9, 2008
i want to access webservice...by creating a proxy of that server..to send a packet.which is in xml..and get a responce from that server.. send me step by step details bcoz i dont have any knowledge about this topic
Reply | Email | Delete | Modify | 
Only works on non complex webservices? by Fernando On March 2, 2009
How about handeling webservices that returns and requests complex objects and multiple classes
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 1999 - 2009  Mindcracker LLC. All Rights Reserved