Invoking a Web Service dynamically using System.Net and SOAP

Purpose

Sometimes, it is required to invoke a web service dynamically. The standard way is to add a web reference to the service from the project and then use the generated proxy client to call the web service. But, it you do not know the web service at the time of creating the project then this cannot be done. Imagine if you have to invoke a web service whose method signature is known to you but the service does not exist. The Url of the web service is available to your program at runtime and it is required to invoke the method. In this situation, you would need to invoke the web service dynamically. This article explains how to invoke the web service dynamically.

Implementation

A web service can be invoked by using HttpWebRequest and HttpWebResponse from the System.Net namespace. We will use these classes to build a dynamic client. All types of web services (.NET (asmx, WCF), Java, PHP) can be called using these two classes. This article will demonstrate how to build a client for ASMX and WCF Services.
We are going to build a client (shown below) which communicates with the web service using SOAP and these classes.

ClientClassDiagram.jpg
 
Web Services communicate by using SOAP. So the first thing to be done is create the SOAP envelope which will be used by HttpWebRequest to invoke the service method.
For this, we start with an empty SOAP envelope.

string _soapEnvelope =
        @"<soap:Envelope
            xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
            xmlns:xsd='http://www.w3.org/2001/XMLSchema'
            xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
        <soap:Body></soap:Body></soap:Envelope>";

Then, we add information to the envelope regarding the method we want to call and the parameters. Note : You can encode the param.Value using HttpUtility.HtmlEncode if you want to send XML as a parameter.

private string CreateSoapEnvelope()
{
   string MethodCall = "<" + this.WebMethod + @" xmlns=""http://tempuri.org/"">";
   string StrParameters = string.Empty;
   foreach (Parameter param in this.Parameters)
   {
       StrParameters = StrParameters + "<" + param.Name + ">" + param.Value + "</" + param.Name + ">";
   }
   MethodCall = MethodCall + StrParameters + "</" + this.WebMethod + ">";
   StringBuilder sb = new StringBuilder(_soapEnvelope);
   sb.Insert(sb.ToString().IndexOf("</soap:Body>"), MethodCall);
   return sb.ToString();
}

After adding the information like the method and parameter values, the SOAP envelope is like below.

<soap:Envelope\r\n  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\r\n  xmlns:xsd='http://www.w3.org/2001/XMLSchema'\r\n  xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>\r\n                
<soap:Body>
<GetCustomer xmlns=\"http://tempuri.org/\">
<CustomerId>ABC123</CustomerId>
</GetCustomer>
</soap:Body>
</soap:Envelope>

Then, we create the HttpWebRequest as shown below. To the header, we add the SOAPAction which is the name of the method we want to invoke. The RequestMethod is set to POST.

private HttpWebRequest CreateWebRequest()
{
   HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(this.Url);
   if (this.WSServiceType == WebServiceClient.ServiceType.WCF)
       webRequest.Headers.Add("SOAPAction", "\"http://tempuri.org/" + this.WCFContractName + "/" + this.WebMethod + "\"");
   else
       webRequest.Headers.Add("SOAPAction", "\"http://tempuri.org/" + this.WebMethod + "\"");
   webRequest.Headers.Add("To", this.Url);
   webRequest.ContentType = "text/xml;charset=\"utf-8\"";
   webRequest.Accept = "text/xml";
   webRequest.Method = "POST";
   return webRequest;
}

Then, we call the web service using HttpWebRequest/HttpWebResponse with the SOAP envelope as shown below. We write the envelope to the request stream. Note : You do not have to do the HttpUtility.HtmlDecode if you are receiving XML as a return from the Web Service.

public string InvokeService()
{
     WebResponse response = null;
     string strResponse = "";
     //Create the request
     HttpWebRequest req = this.CreateWebRequest();
     //write the soap envelope to request stream
     using (Stream stm = req.GetRequestStream())
     {
         using (StreamWriter stmw = new StreamWriter(stm))
         {
             stmw.Write(this.CreateSoapEnvelope());
         }
     }
     //get the response from the web service
     response = req.GetResponse();
     Stream str = response.GetResponseStream();
     StreamReader sr = new StreamReader(str);
     strResponse = sr.ReadToEnd();
     return this.StripResponse(HttpUtility.HtmlDecode(strResponse));
}

Sometimes, it may be required to call the Web Service asynchronously. For this the BeginInvokeService and EndInvokeService API are provided.


public
delegate string DelegateInvokeService();

public void BeginInvokeService(AsyncCallback InvokeCompleted)

{

      DelegateInvokeService Invoke = new DelegateInvokeService(this.InvokeService);

      IAsyncResult result = Invoke.BeginInvoke(InvokeCompleted, null);

}

 

public string EndInvokeService(IAsyncResult result)

{

      var asyncResult = (AsyncResult)result;

      ReturnMessage msg = (ReturnMessage)asyncResult.GetReplyMessage();

 

      return msg.ReturnValue.ToString();

}

Sample Usage


Add a reference to Toolkit.Net.dll

using Toolkit.Net;

In this example, we are calling GetCustomer method of a WCF Service using the Client. We are passing CustomerId parameter to the Service.

For Synchronous call to web service :

List<WebServiceClient.Parameter> lstParameters = new List<WebServiceClient.Parameter>();
lstParameters.Add(new WebServiceClient.Parameter { Name = "CustomerId", Value = "ABC123" });
WebServiceClient client = new WebServiceClient
{
    WebMethod = "GetCustomer",
    Url = "http://x.x.x.x:8080/CustomerService.svc",
    WSServiceType = WebServiceClient.ServiceType.WCF,
    WCFContractName = "ICustomerAgentWS",
    Parameters = lstParameters
};
string returnFromService = client.InvokeService();

For Asynchronous call to web service :

static WebServiceClient client = null;

 

static void Main(string[] args)

{

     List<WebServiceClient.Parameter> lstParameters = new List<WebServiceClient.Parameter>();

     lstParameters.Add(new WebServiceClient.Parameter { Name = "CustomerId", Value = "ABC123" });

 

     client = new WebServiceClient

     {

          WebMethod = "GetCustomer",

          Url = "http://x.x.x.x:8080/CustomerService.svc",

          WSServiceType = WebServiceClient.ServiceType.WCF,

          WCFContractName = "ICustomerAgentWS",

          Parameters = lstParameters

     };

 

     client.BeginInvokeService(InvokeCompleted);

 

     Console.ReadLine();

}

 

public static void InvokeCompleted(IAsyncResult result)

{

     string returnFromService = client.EndInvokeService(result);
}


Parameters of the Client like the Url, Method etc. can be set at run-time. The advantage is that this Client can be used to call an ASMX, WCF, Java and PHP web service since HttpWebRequest/HttpWebResponse can be used to POST to any type of web service. 

Note: To be able to invoke a WCF Service using this method the binding has to be basicHttpBinding.

You can download the Client as a free download.


Similar Articles