|
|
|
|
|
|
|
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
|
|
|
Similar ArticlesMost ReadTop RatedLatest
|
|
Related EbooksTop Videos
|
|
|
Description
|
|
The Complete Visual C# Programmer's Guide, written by the authors of C# Corner, covers most of the major components that make up C# and the .NETenvironment including Windows Forms, ADO.NET, GDI+, Web Services, and Security. The book is geared toward the beginner to intermediate programmers.
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
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.
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|