Monday, March 15, 2010

Using Get & Post from Silverlight to a RESTful service

A recent project I was working on needed a lot of data to be passed quickly between a Silverlight client and a .Net server. The approach taken, instead of using a relatively-heavy XML SOAP implementation, was to go with a lightweight JSON REST web service.

Information on the web is moderately clear on how this works, but I found the POST part of the picture to be virtually non-existent. As I would be passing in more data than some browsers support through the querystring, I didn't have any other option: I had to figure it out. Thus, if you need that info, read on...

GET

The following is a sample web service with one GET method:


    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyTestService
    {
        [OperationContract(Name = "MyTestMethod")]
        [WebGet(UriTemplate = "myTestMethod/{aParameter}", ResponseFormat = WebMessageFormat.Json)]
        public string MyTestMethod(string aParameter)
        {
            return aParameter;
        }
    }


The Silverlight call could be similar to:


        string parameter = "Hello!";
        string serviceUriFormat = "http://someDomain/MyTestService.svc/myTestMethod/{0}";
        Uri serviceUri = new Uri(String.Format(serviceUriFormat, parameter), UriKind.Absolute);
        WebClient webClient = new WebClient();
        webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(myOpenReadCompletedEventHandler);
        webClient.OpenReadAsync(serviceUri, myCallback);

POST

While GET is straightforward POST is more complex as, it turns out, POST methods have an additional input Stream.
The following is a sample POST web service call which calls a method with a parameter and with an additional stream of POST data:


    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyTestService
    {
        [OperationContract(Name = "MyTestMethodPost")]
        [WebInvoke(UriTemplate = "myTestMethodPost/{aParameter}", Method = "POST")]
        public string MyTestMethodPost(string aParameter, Stream input)
        {
            string testParameter = aParameter;
            string sentence;
            using (StreamReader sr = new StreamReader(input))
            {
                sentence = sr.ReadToEnd();
            }
            return sentence;
        }
    }


Notice that the method is marked with Method="Post" and that it has an extra parameter of type Stream.

The associated Silverlight call would be as follows:


        string parameter = "test";
        string postData = "A really really really long string";
        string serviceUriFormat = "http://someDomain/MyTestService.svc/MyTestMethodPost/{0}";
        Uri serviceUri = new Uri(String.Format(serviceUriFormat, parameter), UriKind.Absolute);
        WebClient webClient = new WebClient();
        webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(uploadStringCompletedEventHandler);
        webClient.Headers["Content-type"] = "application/xml";
        webClient.Encoding = Encoding.UTF8;
        webClient.UploadStringAsync(serviceUri, "POST", postData, callback);


Notice here that instead of using the OpenReadAsync method of the WebClient class, we use the UploadStringAsync method with the appropriate Encoding and Content Header set.

By using the above POST approach I was able to post relatively large amounts of data to the REST web service method