Echo Service

I l @ ve RuBoard

Building client/server applications generally seems to resemble the chicken and egg problem. You need a server to test the client and a client to test the server. An echo server will bounce back any information it receives to the client who sent the data. Echo servers allow for a client application to test its connection and ensure that the data being sent is correct. Implementing an EchoService using .NET WebServices will serve as a good starting point.

VS.NET supports creation of WebServices directly from the IDE. VS.NET will connect to the specified IIS server using FrontPage extensions to open a Web folder, just as with the WebApplication developed in the previous chapters. Figure 4.4.1 shows the new project dialog for creating a WebService .

Figure 4.4.1. WebService VS.NET project.

graphics/0404fig01.gif

WebServices, as created by VS.NET, consists of two files ”an asmx file and a C# source file. However, only the asmx file is necessary because all the C# code can reside within the asmx file. However, this is not the approach that will be taken when developing the examples presented in this chapter. Listing 4.4.1 shows the C# code for the EchoService WebService . In addition, I've changed the name of the default class from Service1 to EchoServer for clarity.

Listing 4.4.1 The EchoService.asmx.cs Source
 1: using System;  2: using System.Collections;  3: using System.ComponentModel;  4: using System.Data;  5: using System.Diagnostics;  6: using System.Web;  7: using System.Web.Services;  8:  9: namespace EchoService 10: { 11:     /// <summary> 12:     /// Summary description for EchoServer. 13:     /// </summary> 14:     public class EchoServer : System.Web.Services.WebService 15:     { 16:         public EchoServer() 17:         { 18:             //CODEGEN: This call is required by the ASP.NET Web Services Designer 19:             InitializeComponent(); 20:         } 21: 22:         #region Component Designer generated code 23:         /// <summary> 24:         /// Required method for Designer support - do not modify 25:         /// the contents of this method with the code editor. 26:         /// </summary> 27:         private void InitializeComponent() 28:         { 29:         } 30:         #endregion 31: 32:         /// <summary> 33:         /// Clean up any resources being used. 34:         /// </summary> 35:         protected override void Dispose( bool disposing ) 36:         { 37:         } 38: 39: 40:         [WebMethod] 41:         public string Echo( string Message ) { 42:             return Message; 43:         } 44: 45:         // WEB SERVICE EXAMPLE 46:         // The HelloWorld() example service returns the string Hello World 47:         // To build, uncomment the following lines then save and build the project 48:         // To test this web service, press F5 49: 50: //      [WebMethod] 51: //       public string HelloWorld() 52: //       { 53: //           return "Hello World"; 54: //       } 55:     } 56: } 

The EchoService.asmx.cs in Listing 4.4.1 lays out a basic WebService. The EchoServer class inherits from System.Web.Services.WebService base class. The WebService base class does not require the derived class to override or implement any methods ; instead, the WebService base class merely provides the implementation necessary to support the method invocations.

The EchoServer class itself provides only a single method that is exposed to clients ”Echo. Notice the attribute used on the method. This [WebMethod] attribute is used by the runtime to locate methods that the class exposes to clients. In the same way that the DBAccess class implemented for the EmployeeBrowser example, the runtime makes use of the Reflection API to locate a WebMethod and invoke it with the proper parameters.

For IIS to host the WebService, there needs to exist an asmx file in the virtual Web directory containing the EchoService WebService . This asmx file is used to wire-up the WebService code to the .dll and class providing the actual WebService. This basic wiring of the asmx file to the necessary C# code is shown in Listing 4.4.2.

Listing 4.4.2 EchoService.asmx
 1: <%@ WebService Language="c#" 2:                Codebehind="EchoService.asmx.cs" 3:                Class="EchoService.EchoService" %> 4: 

As you can see, there really isn't too much to the asmx file. As with aspx pages, there is a language directive, a Codebehind directive, that specifies the source file and a class directive that gives the qualified name of the class providing the WebService implementation.

Because the EchoService has been implemented as a C# source file and not inline script, the EchoService.asmx.cs file needs to be compiled and the resulting .dll copied to the bin directory of the current virtual directory hosting the EchoService . VS.NET will automatically perform this process during the build process. Testing the service merely requires accessing the asmx file with the Internet Explorer Web browser, as show in Figure 4.4.2.

Figure 4.4.2. EchoService.asmx within Internet Explorer.

graphics/0404fig02.gif

The WebServices system creates a basic HTML form from which the WebService can be tested . Locate the input box, enter some text, and click the Invoke button. This will cause the Echo method to be invoked and the return XML will be displayed in the browser (see Figure 4.4.3).

Figure 4.4.3. Result of Echo with "Hello World."

graphics/0404fig03.gif

Building a Proxy Class

To invoke the WebService from C# code, a proxy class needs to exist. A proxy represents a gateway to the actual server code. In turn , the server has a stub to which the proxy connects. Figure 4.4.4 is a simple illustration of this.

Figure 4.4.4. The proxy-stub relationship.

graphics/0404fig04.gif

To create a proxy, you need to determine the particular protocol you want to use. The choices are HTTP POST, HTTP GET, or SOAP. Based on that decision, the proxy can then be derived from the property base class from System.Web.Services.Protocols . X , where X represents the corresponding protocol.

Listings 4.4.3 through 4.4.5 show the implementation for SOAP, HTTP POST, and HTTP GET, respectively. The basic structure for each implementation is the same. The differences to note include the base classes and the attributes applied to the Echo method and the parameter. Using the proxy object is no different than using any other object. Instantiate an instance and invoke the method with the proper parameter specified.

Listing 4.4.3 Basic EchoService Proxy Class: SOAP
 1: //------------------------------------------------------------------------  2: // <autogenerated>  3: //     This code was generated by a tool.  4: //     Runtime Version: 1.0.2914.11  5: //  6: //     Changes to this file may cause incorrect behavior and will be lost if  7: //     the code is regenerated.  8: // </autogenerated>  9: //------------------------------------------------------------------------ 10: 11: // 12: // This source code was auto-generated by wsdl, Version=1.0.2914.11. 13: // 14: using System.Diagnostics; 15: using System.Xml.Serialization; 16: using System; 17: using System.Web.Services.Protocols; 18: using System.Web.Services; 19: 20: 21: [System.Web.Services.WebServiceBindingAttribute(Name="EchoServerSoap", graphics/ccc.gif Namespace="http://tempuri.org/")] 22: public class EchoServer : System.Web.Services.Protocols.SoapHttpClientProtocol { 23: 24:     [System.Diagnostics.DebuggerStepThroughAttribute()] 25:     public EchoServer() { 26:         this.Url = "http://localhost/EchoService/EchoService.asmx"; 27:     } 28: 29:     [System.Diagnostics.DebuggerStepThroughAttribute()] 30:     [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/ graphics/ccc.gif Echo", Use=System.Web.Services.Description.SoapBindingUse.Literal, graphics/ccc.gif ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] 31:     public string Echo(string Message) { 32:         object[] results = this.Invoke("Echo", new object[] { 33:                     Message} ); 34:         return ((string)(results[0])); 35:     } 36: 37:     [System.Diagnostics.DebuggerStepThroughAttribute()] 38:     public System.IAsyncResult BeginEcho(string Message, System.AsyncCallback graphics/ccc.gif callback, object asyncState) { 39:         return this.BeginInvoke("Echo", new object[] { 40:                     Message} , callback, asyncState); 41:     } 42: 43:     [System.Diagnostics.DebuggerStepThroughAttribute()] 44:     public string EndEcho(System.IAsyncResult asyncResult) { 45:         object[] results = this.EndInvoke(asyncResult); 46:         return ((string)(results[0])); 47:     } 48: } 
Listing 4.4.4 Basic EchoService Proxy Class: HTTP POST
 1: //---------------------------------------------------------------------  2: // <autogenerated>  3: //     This code was generated by a tool.  4: //     Runtime Version: 1.0.2914.11  5: //  6: //     Changes to this file may cause incorrect behavior and will be lost if  7: //     the code is regenerated.  8: // </autogenerated>  9: //--------------------------------------------------------------------- 10: 11: // 12: // This source code was auto-generated by wsdl, Version=1.0.2914.11. 13: // 14: using System.Diagnostics; 15: using System.Xml.Serialization; 16: using System; 17: using System.Web.Services.Protocols; 18: using System.Web.Services; 19: 20: 21: public class EchoServer : System.Web.Services.Protocols.HttpPostClientProtocol { 22: 23:     [System.Diagnostics.DebuggerStepThroughAttribute()] 24:     public EchoServer() { 25:         this.Url = "http://localhost/EchoService/EchoService.asmx"; 26:     } 27: 28:     [System.Diagnostics.DebuggerStepThroughAttribute()] 29:    [System.Web.Services.Protocols.HttpMethodAttribute( graphics/ccc.gif typeof(System.Web.Services.Protocols.XmlReturnReader), graphics/ccc.gif typeof(System.Web.Services.Protocols.HtmlFormParameterWriter))] 30:     [return: System.Xml.Serialization.XmlRootAttribute("string", Namespace="http:// graphics/ccc.gif tempuri.org/", IsNullable=true)] 31:     public string Echo(string Message) { 32:         return ((string)(this.Invoke("Echo", (this.Url + "/Echo"), new object[] { 33:                     Message} ))); 34:     } 35: 36:     [System.Diagnostics.DebuggerStepThroughAttribute()] 37:     public System.IAsyncResult BeginEcho(string Message, System.AsyncCallback graphics/ccc.gif callback, object asyncState) { 38:         return this.BeginInvoke("Echo", (this.Url + "/Echo"), new object[] { 39:                     Message} , callback, asyncState); 40:     } 41: 42:     [System.Diagnostics.DebuggerStepThroughAttribute()] 43:     public string EndEcho(System.IAsyncResult asyncResult) { 44:         return ((string)(this.EndInvoke(asyncResult))); 45:     } 46: } 
Listing 4.4.5 Basic EchoService Proxy Class: HTTP GET
 1: //------------------------------------------------------------------------  2: // <autogenerated>  3: //     This code was generated by a tool.  4: //     Runtime Version: 1.0.2914.11  5: //  6: //     Changes to this file may cause incorrect behavior and will be lost if  7: //     the code is regenerated.  8: // </autogenerated>  9: //------------------------------------------------------------------------ 10: 11: // 12: // This source code was auto-generated by wsdl, Version=1.0.2914.11. 13: // 14: using System.Diagnostics; 15: using System.Xml.Serialization; 16: using System; 17: using System.Web.Services.Protocols; 18: using System.Web.Services; 19: 20: 21: public class EchoServer : System.Web.Services.Protocols.HttpGetClientProtocol { 22: 23:     [System.Diagnostics.DebuggerStepThroughAttribute()] 24:     public EchoServer() { 25:         this.Url = "http://localhost/EchoService/EchoService.asmx"; 26:     } 27: 28:     [System.Diagnostics.DebuggerStepThroughAttribute()] 29:     [System.Web.Services.Protocols.HttpMethodAttribute( graphics/ccc.gif typeof(System.Web.Services.Protocols.XmlReturnReader), typeof(System.Web.Services.Protocols.UrlParameterWriter))] 30:     [return: System.Xml.Serialization.XmlRootAttribute("string", Namespace="http:// graphics/ccc.gif tempuri.org/", IsNullable=true)] 31:     public string Echo(string Message) { 32:         return ((string)(this.Invoke("Echo", (this.Url + "/Echo"), new object[] { 33:                     Message} ))); 34:     } 35: 36:     [System.Diagnostics.DebuggerStepThroughAttribute()] 37:     public System.IAsyncResult BeginEcho(string Message, System.AsyncCallback graphics/ccc.gif callback, object asyncState) { 38:         return this.BeginInvoke("Echo", (this.Url + "/Echo"), new object[] { 39:                     Message} , callback, asyncState); 40:     } 41: 42:     [System.Diagnostics.DebuggerStepThroughAttribute()] 43:     public string EndEcho(System.IAsyncResult asyncResult) { 44:         return ((string)(this.EndInvoke(asyncResult))); 45:     } 46: } 

The good news is that you don't have to type this code by hand. Shipped along with the .NET SDK is a tool named WSDL.exe whose purpose is to generate client code for WebServices. Running WSDL.exe with no parameters will display the command lines and their meanings. The basic form for WSDL looks like the following:

 WSDL <url or wsdl document> <language> <protocol> 

WebServices can be invoked synchronously or asynchronously. The common case is to use the synchronous method invocation. Asynchronous method invocation is useful in cases where the method call requires an extended time period for processing, the idea being that the client can continue to work while waiting for the async-response to return. This allows for other work to be performed without tying up the client application.

The various bindings ”SOAP, HTTP GET, and HTTP POST ”present an opportunity to develop a client application that utilizes a factory to create and return the requested binding. A WebService is capable of exposing several objects, and each of those objects can be viewed in terms of a public interface. This being the case, an interface that defines the public methods of a WebService can then be defined, and then each proxy implements this interface.

With respect to the EchoService, the interface for the WebService itself can be expressed as follows :

 public interface IEchoService {     string Echo( string Message ); } 

This interface is conceptual only; in actual fact, the EchoService does not have an interface that can be referenced.

ProxyFactory

The Factory pattern helps to isolate clients from the underlying type that a factory creates. The Factory returns a well-known type ”in this case, the IEchoService interface. Each proxy, as shown in Listings 4.4.3, 4.4.4 and 4.4.5, needs to implement the IEchoService interface. Actually, the listed proxies already do and only need the IEchoService interface added to the class declaration.

The ProxyFactory class will take an enum as a parameter to determine the underlying object to create and return. Listing 4.4.6 shows the ProxyFactory implementation.

Listing 4.4.6 ProxyFactory.cs
 1: namespace ProxyFactory  2: {  3:     using System;  4:  5:  6:     ///<summary>  7:     /// Generalize the WebService as an interface to be implemented by each proxy  8:     ///</summary>  9:     public interface IEchoService 10:     { 11:         string Echo( string Message ); 12:     } 13: 14: 15:     ///<summary> 16:     ///Enumerate the Various Bindings 17:     ///</summary> 18:     public enum ProxyProtocol 19:     { 20:         SOAP, 21:         HttpGet, 22:         HttpPost 23:     } 24: 25: 26:     ///<summary> 27:     ///The Factory pattern can be used to construct a proxy for the requested binding. 28:     ///The Factory will then return a well known interface to the web service 29:     ///</summary> 30:     public class ProxyFactory 31:     { 32:         public static IEchoService ConstructProxy( ProxyProtocol protocol ) 33:         { 34: 35:             switch( protocol ) 36:             { 37:                  case ProxyProtocol.SOAP: 38:                      return (IEchoService)new EchoServiceSoapProxy(); 39: 40: 41:                  case ProxyProtocol.HttpGet: 42:                      return (IEchoService)new EchoServiceHttpGetProxy(); 43: 44: 45:                  case ProxyProtocol.HttpPost: 46:                      return (IEchoService)new EchoServiceHttpPostProxy(); 47: 48: 49:                  default: 50:                      throw new System.Exception( "Invalid Argument" ); 51: 52:              } 53:         } 54:     } 55: } 56: 

The ProxyFactory class encapsulates the creation of the requested proxy based on the binding passed into to the ConstructProxy method. Because the IEchoService interface has been implemented by each of the underlying proxy implementations , the ProxyFactory needs only to construct the proper proxy class and return the IEchoService interface.

Windows Forms Client

To put the WebService to the test, the following WinForms client provides a UI interface to the EchoService . Using VS.NET, create a C# Windows Forms application and construct the form shown in Figure 4.4.5.

Figure 4.4.5. WinForms client.

graphics/0404fig05.gif

Because the forms designer generates most of the code, only the relevant sections need to be reviewed rather than the complete code listing. Add the following protected variable:

 private ProxyProtocol    SelectedProtocol=ProxyProtocol.SOAP; 

This will allow the application to track the requested protocol for the WebService method invocation. Be sure to include the using ProxyFactory in the namespace declarations.

Next , I've hooked up each radio button to the same Click handler, which contains the following code:

 protected void OnOpt_Click( object sender, EventArgs e ) {     if( sender == optSOAP)         this.SelectedProtocol = ProxyProtocol.SOAP;     else if( sender == optHttpPost )         this.SelectedProtocol = ProxyProtocol.HttpPost;     else         this.SelectedProtocol = ProxyProtocol.HttpGet; } 

This code serves to update the SelectedProtocol variable based on the currently selected radio button. Refer to the WinForms chapters for assigning delete handlers for controls.

Lastly, the invocation of the WebService happens when the Invoke button raises a Click event.

 protected void btnInvoke_Click (object sender, System.EventArgs e) {     //Get an IEchoService interface     IEchoService echo         = ProxyFactory.ConstructProxy( this.SelectedProtocol );     this.lblResponse.Text = echo.Echo( this.txtMessage.Text );     this.txtMessage.Text = ""; } 

With the event handler code in place, the Windows Forms client is ready to go. Figure 4.4.6 shows the client application ready to invoke the Echo WebService . In Figure 4.4.7, the result of the WebService invocation is displayed in the appropriate label showing the text echoed back from the EchoService .

Figure 4.4.6. Before clicking the WebService Invoke button.

graphics/0404fig06.gif

Figure 4.4.7. After clicking the WebService Invoke button.

graphics/0404fig07.gif

Now that the common case of using the synchronous method invocation has been discussed, the next step is to understand the asynchronous method invocation.

Asynchronous WebService invocation requires a delegate with the following method signature:

 <access modifier> void <name>( System.IAsyncResult ) 

Fortunately, or unfortunately , there is no dark secret to how this mechanism works. The basic process for the call is the same as the synchronous version, only now a thread is created to handle the process of sending and receiving the request. After the request has been completed, an event is fired and the attached delegate is called.

Look at the various generated WebService proxies in Listings 4.4.3, 4.4.4 and 4.4.5; each proxy defines the following method:

 public System.IAsyncResult BeginEcho(string message,                                      System.AsyncCallback callback,                                      object asyncState) 

Just as the Echo method takes a string parameter, so does the BeginEcho method. In addition, the BeginEcho method requires a delegate for the callback and an object to which the asynchronous request belongs. Rather than list an entire application to invoke this method, the source code snippet in Listing 4.4.7 demonstrates the use of the asynchronous method invocation.

Listing 4.4.7 Asynchronous WebService Invocation
 1: //Create the proxy and invoke the BeginEcho method 2: EchoServiceSoapProxy sp = new EchoServiceSoapProxy( ); 3: sp.BeginEcho("Hello", new System.AsyncCallback(this.CallBack), sp ); 4: 5: //The callback for the asynchronous result 6: protected void CallBack( System.IAsyncResult ar )       { 7:      EchoServiceSoapProxy sp = (EchoServiceSoapProxy)ar.AsyncState; 8:      this.button1.Text = sp.EndEcho( ar ); 9: } 

After the CallBack method is invoked, the original EchoServiceSoapProxy object is obtained from the IAsyncResult interface. Next, the EndEcho method is called to obtain the results of the asynchronous WebService method call .

Hopefully the EchoService is a good introduction to the ease of WebService development and client consumption of such WebServices.

I l @ ve RuBoard


C# and the .NET Framework. The C++ Perspective
C# and the .NET Framework
ISBN: 067232153X
EAN: 2147483647
Year: 2001
Pages: 204

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net