Friday, May 4, 2012

Using wget to call WCF services

There are a number of general purpose tools created to perform calls to WCF services, like SoapUI and WCFStorm. Visual Studio also ships with one - WcfTestClient, but this one is very simple and has no features for automation. Some times you just want to perform a low level call, or you want to make WCF-calls from a BAT/CMD file. Why download and install a multi-megabyte packaget when this should be as simple as performing a fairly standard HTTP GET request?

wget is a utility that should be in any web developer's tool box. If you're on linux this is a no-brainer (apt-get install wget), but you can also get this on windows. Heard of gnuwin32? You can use the download manager getgnuwin32 to get the entire package, or you can just download what's needed for wget. If not, make sure you download both the binaries and the dependencies zip file.

Using BasicHttpBinding (SOAP 1.1)

As an example, let's say you've created the following service
    [ServiceContract(Namespace = "http://tepmuri.org/myservice/")]
    public interface IService
    {
        [OperationContract]
        string Hello(string name);
    }

    public class Service : IService
    {
        public string Hello(string name)
        {
            return "Hello " + name;
        }
    }
It's a simple Hello World service that takes a string for input and returns one as output. Let's also make the following assumptions:
  • You're serving it at http://localhost/Service.svc
  • You're using BasicHttpBinding
You then need to synthesize the SOAP input XML. If you think that sounds boring, you could always copy it from an actual call made with a thicker client (like a .NET program or WcfTestClinet). Fiddler can help you with this.

For the example service given above under the given circumstances (BasicHttpBinding), this is a valid SOAP 1.1 XML input:

 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
   <Hello xmlns="http://tepmuri.org/myservice/">
    <name>world</name>
   </Hello>
  </s:Body>
 </s:Envelope>

Let's say you've saved this text into a file called hello.xml. To perform the call, execute the following wget command from the command line:

wget --post-file "hello.xml" --header "content-type: text/xml;charset=utf-8" --header "SOAPAction: http://tepmuri.org/myservice/IService/Hello" http://localhost/Service.svc

There's really not much to it We need four parameteres

  1. The input XML as a filename
  2. A HTTP header specifying the content-type
  3. A HTTP header specifying the SOAP Action name with full namespace
  4. URL of the service

If you've done everything correctly, you should get a 200 return code and the SOAP XML Body in the HTTP Body.

Using WsHttpBinding (SOAP 1.2)

Now if you're using WsHttpBinding, you need a slightly more complex input XML. Use this as a starting point:

 <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
   <a:Action s:mustUnderstand="1" >http://tepmuri.org/myservice/IService/Hello</a:Action>
   <a:MessageID>urn:uuid:149c449f-0593-416b-ae1f-b73308a6e2d2</a:MessageID>
   <a:ReplyTo>
    <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
   </a:ReplyTo>
   <a:To s:mustUnderstand="1">http://localhost/Service.svc</a:To>
  </s:Header>
  <s:Body>
   <Hello xmlns="http://tepmuri.org/myservice/">
    <name>world</name>
   </Hello>
  </s:Body>
 </s:Envelope>

In addition you'll need to change the content-type HTTP header of the wget command to the following:

-header "Content-Type: application/soap+xml; charset=utf-8" 

A couple of more wget paramteres that might come handy:

  • HTTPS: If you're exposing the service with HTTPS security and you have a temporary SSL Certificate, use --no-check-certificate
  • Proxy: Wget takes proxy credentials on the command line, but the URL is specified by an environment variable named http_proxy. Running SET http_proxy=localhost:8888 on the command line before wget will redirect all commands through a proxy (like i.e. fiddler)

1 comment: