only for RuBoard |
Now that you have seen how to create and expose a web service, this section looks at creating clients to consume these web services.
At the time of writing, the WSDL specification (available at www.w3.org/TR/wsdl) is an acknowledged submission to W3C and is supported by industry leaders like Microsoft, IBM and Ariba. According to this specification, the official definition of Web Service Description Language (WSDL) states that, "WSDL is an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information." If you are familiar with the COM and CORBA component models, you can relate this to the Interface Description Language (IDL) file that describes the interfaces exposed by the components in a platform-neutral manner. Similar to IDL, a WSDL file defines a contract between a client and a server (a web service, in this case).
SOAP messages do carry type information; therefore, SOAP allows for dynamic determination of type. But without WSDL, it's impossible to call a function correctly unless the client application developer knows the name and the parameters in advance by reading the documentation or by examining wire messages. WSDL enables us to eliminate the human intervention required for acquiring the knowledge of the method names and the parameters, as it is possible to automate the generation of proxies for web services. Moreover, this happens in a truly language- and platform-independent way because WSDL uses XML syntax to describe the web service.
The SOAP Toolkit 2.0 provides a WSDL generation wizard that takes the COM component as the input and generates a WSDL file for you. A small drawback with this is that, if anything changes in the method signatures in the component, you must redo the process of the WSDL file generation. In an ASP.NET web service, you avoid the task of generating the WSDL file from the wizard. This is because when you simply append ?WSDL to the .asmx document URL, you get a WSDL file. Because this is generated dynamically by inspecting the methods marked with the WebMethod attribute in the .asmx file for every request, this is guaranteed to reflect the latest modifications in the .asmx file. Figure 11.4 shows the WSDL generated for the Calculator web service.
The generated WSDL in Figure 11.4 describes the HTTP-GET , HTTP-POST , and SOAP protocols. You must look at the part of the WSDL document that describes the bindings for the SOAP protocol and try to understand the document structure. Listing 11.3 shows the WSDL using SOAP.
<?xml version="1.0" encoding="utf-8" ?> <definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s0="http://tempuri.org/"targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/"> <s:element name="Add"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="a" type="s:int" /> <s:element minOccurs="1" maxOccurs="1" name="b" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="AddResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="AddResult" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="Subtract"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="a" type="s:int" /> <s:element minOccurs="1" maxOccurs="1" name="b" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="SubtractResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="SubtractResult" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="int" type="s:int" /> </s:schema> </types> <message name="AddSoapIn"> <part name="parameters" element="s0:Add" /> </message> <message name="AddSoapOut"> <part name="parameters" element="s0:AddResponse" /> </message> <message name="SubtractSoapIn"> <part name="parameters" element="s0:Subtract" /> </message> <message name="SubtractSoapOut"> <part name="parameters" element="s0:SubtractResponse" /> </message> <portType name="CalculatorSoap"> <operation name="Add"> <input message="s0:AddSoapIn" /> <output message="s0:AddSoapOut" /> </operation> <operation name="Subtract"> <input message="s0:SubtractSoapIn" /> <output message="s0:SubtractSoapOut" /> </operation> </portType> <binding name="CalculatorSoap" type="s0:CalculatorSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="Add"> <soap:operation soapAction="http://tempuri.org/Add" style="document" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> <operation name="Subtract"> <soap:operation soapAction="http://tempuri.org/Subtract" style="document" / > <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <service name="Calculator"> <port name="CalculatorSoap" binding="s0:CalculatorSoap"> <soap:address location="http://localhost/WebServices/Calculator/Calculator.asmx" /> </port> </service> </definitions>
Five major sections are in the WSDL document, which can be categorized into two groups. The top group is comprised of abstract definitions, and the bottom group consists of concrete descriptions. The abstract sections define SOAP messages in a platform- and language-independent manner; they do not contain any machine- nor language-specific elements. This helps define a set of services that several diverse websites can implement. Site-specific matters, such as serialization, are then relegated to the bottom sections, which contain concrete descriptions.
The following abstract definitions are found in the WSDL file:
Types ” The <types> element contains the parameters names and data type definitions for parameters and the return types of web service methods. This type definition is based on an XSD schema. This schema is referenced from the Messages section of the document.
Messages ” If you consider operations as functions, then a <message> element defines the parameters to that function. Each <part> child element in the <message> element corresponds to a parameter. Input parameters are defined in a single <message> element, separate from output parameters, which are in their own <message> element. Parameters that are both input and output have their corresponding <part> elements in both input and output <message> elements. The name of an output <message> element ends in Response , as in AddResponse , by convention. Each <part> element has name and type attributes, just as a function parameter has both a name and type.
PortTypes ” Refers to message definitions in a Messages section to describe function signatures (such as operation name, input parameters, and output pameters). Operation elements within a PortType define the syntax for calling all methods in the PortType . Each <portType> element groups together a number of related operations. In an <operation> element, there can be at most one <input> element, at most one <output> element, and at most one <fault> element. Each of these three elements has name and message attributes.
The following abstract definitions are found in the WSDL file:
Bindings ” The Binding section is where the protocol, serialization, and encoding on the wire corresponding to the operations defined in the portType element are fully specified. The style attribute in the <binding> element specifies the message serialization format as SOAP Section 5 RPC-style or document-style encoding. The <soap:operation> within the <operation> element specifies the value of the SOAPAction HTTP header. The <input> and <output> elements within the <operation> element specify how the input and output messages of the individual operation are encoded.
Services ” Specifies port address(es) of each binding. Therefore, a service is a set of <port> elements. Each <port> element associates a location with a <binding> in a one-to-one fashion. If more than one <port> element is associated with the same <binding> , the additional URL locations can be used as alternates. More than one <service> element can exist in a WSDL document.
Note
You can find the WSDL 1.1 Specification that has been submitted to the W3C as a Note at www.w3.org/TR/wsdl.
For the development of the client of web services,Visual Studio .NET provides a new option to add a web reference to the web service. With this easy-to-use option, a proxy class is automatically generated. The Add Web Reference option is available from the Project menu or by right-clicking References in the Solution Explorer. Figure 11.5 shows a web reference being added in a Visual Studio .NET project.
Figure 11.6 shows the Solution Explorer with the added web reference.
Now that you have a proxy automatically generated, use it to build a client. You can design a user interface for the client, as shown in Figure 11.7.
The following code for the Add and Subtract button click events shows the use of the proxy to access the Calculator web service:
//Add private void Button1_Click(object sender, System.EventArgs e) { CalculatorService.Calculator calc = new CalculatorService.Calculator(); Label4.Text = calc.Add(Int32.Parse(TextBox1.Text),Int32.Parse(TextBox2.Text)).ToString(); } //Subtract private void Button2_Click(object sender, System.EventArgs e) { CalculatorService.Calculator calc = new CalculatorService.Calculator(); Label4.Text = calc.Subtract(Int32.Parse(TextBox1.Text),Int32.Parse (TextBox2.Text)).ToString(); }
Figure 11.8 shows the result of clicking the Add button.
Running the WSDL tool with the following code creates a source file named Calculator.cs for the proxy object:
wsdl /o:Calculator.cs http://localhost/WebServices/Calculator/Calculator.asmx?WSDL
Figure 11.9 shows the WSDL tool being used to create a proxy class.
For more information on all the options available for the WSDL tool refer to Chapter 4, "XML Tool Support in Visual Studio .NET."
Listing 11.4 shows the auto-generated proxy source created by the WSDL tool. Note that the proxy class generated by Visual Studio .NET has the same code as that's generated by the WSDL tool.
using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; /// <remarks/> [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Web.Services.WebServiceBindingAttribute(Name="CalculatorSoap", Namespace="http:// tempuri.org/")] public class Calculator : System.Web.Services.Protocols.SoapHttpClientProtocol { /// <remarks/> public Calculator() { this.Url = "http://localhost/WebServices/Calculator/Calculator.asmx"; } /// <remarks/> [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/Add", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public int Add(int a, int b) { object[] results = this.Invoke("Add", new object[] { a, b}); return ((int)(results[0])); } /// <remarks/> public System.IAsyncResult BeginAdd(int a, int b, System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("Add", new object[] { a, b}, callback, asyncState); } /// <remarks/> public int EndAdd(System.IAsyncResult asyncResult) { object[] results = this.EndInvoke(asyncResult); return ((int)(results[0])); } /// <remarks/> [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/Subtract", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public int Subtract(int a, int b) { object[] results = this.Invoke("Subtract", new object[] { a, b}); return ((int)(results[0])); } /// <remarks/> public System.IAsyncResult BeginSubtract(int a, int b, System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("Subtract", new object[] { a, b}, callback, asyncState); } /// <remarks/> public int EndSubtract(System.IAsyncResult asyncResult) { object[] results = this.EndInvoke(asyncResult); return ((int)(results[0])); } }
The proxy class inherits from the System.Web.Services.Protocols. SoapHttpClientProtocol base class. The generated proxy class and the SoapHttpClientProtocol class wrap up the details of sending and receiving SOAP requests and responses to the web service, thereby making the client developer's job trivial. The client developer does not need to understand the subtleties of the SOAP message structure unless he or she wants low-level control over SOAP messages being serialized and deserialized.
Table 11.3 contains some useful public properties of the proxy object that it inherits from the SoapHttpClientProtocol class. The clients can make use of these properties to alter the default behavior of the generated proxy classes.
Property | Description |
---|---|
AllowAutoRedirect | Gets or sets whether the client automatically follows server redirections. If a client sends authentication information, such as a username and password, you do not want to enable the server to redirect because it might compromise security. |
ClientCertificates | Gets the collection of client certificates. This property allows a client to pass one or more client certificates, also known as Authenticode X.509 v.3 certificates, when calling an XML Web Service method. If the XML Web Service method is configured to use client certificates, a client certificate can be used as one mechanism for authenticating a client. |
CookieContainer | Gets or sets the collection of cookies. If an XML Web Service method uses session state, a cookie is passed back in the response headers to the XML Web Service client that uniquely identifies the session for that XML Web Service client. In order for the XML Web Service client to receive that cookie, a new instance of CookieContainer must be created and assigned to the CookieContainer property before the XML Web Service method is called. This ensures that the cookie is properly included in subsequent requests. |
Credentials | Gets or sets security credentials for web service client authentication. When using the Credentials property, an XML Web Service client must instantiate a class implementing ICredentials , such as NetworkCredential , and set the client credentials specific to the authentication mechanism. The NetworkCredential class can set authentication credentials by using the basic, digest, NTLM, and Kerberos authentication mechanisms. |
PreAuthenticate | When PreAuthenticate is true, the WWW-authenticate header is sent with the first request if the authentication mechanism supports doing so. When PreAuthenticate is false, a request is made to the XML Web Service method without initially attempting to authenticate the user. If the XML Web Service allows anonymous access, the XML Web Service method is executed. If anonymous access is disallowed , a 401 HTTP return code is sent back to the client. In response, the WebClientProtocol class returns authentication credentials to the web server. If the client is authenticated and subsequently authorized to access the XML Web Service, the XML Web Service method is executed; otherwise , the client is denied access. |
Proxy | Gets or sets proxy information for making an XML Web Service request through a firewall. Use the proxy property if a client needs to use different proxy settings than those in the system settings. You can use the WebProxy class to set the proxy settings because it implements IWebProxy . |
Timeout | Indicates the time an XML Web Service client waits for a synchronous XML Web Service request to complete (in milliseconds ). Setting the Timeout property to Timeout.Infinite indicates that the request doesn't time out. Although an XML Web Service client can set the Timeout property to not time out, the web server can still cause the request to time out on the server side. |
Url | Gets or sets the base URL of the XML Web Service that the client is requesting. The Url property can be changed to refer to any XML Web Service that implements the same service description from which the proxy class was generated. |
UserAgent | Gets or sets the value for the user agent header that's sent with each request. |
only for RuBoard |