Vishful thinking…

Making HTTP Post with Silverlight a little easier

Posted in C#, Silverlight by viswaug on September 29, 2009

In my earlier post, I talked about how to go about making an HTTP Post in Silverlight. The whole process is a little complicated to say the least. Silverlight does not come with an utility class like WebClient that makes making a HTTP Get a little easier. So, I created a class called ‘HTTPPostHelper’ that does for HTTP Post what WebClient does for HTTP Get. The ‘HTTPPostHelper’ class listed below, makes the whole process a lot easier. To use the class, do the following

  • Create an instance of the HTTPPostHelper class with the type of the input class (T) and the type of the returned class (K) as the generic arguments.
  • Listen to the ‘PostCompleted’ event that gets called when the Post operation completes successfully. The EventArgs of the event contains an instance of the return class type.
  • Listen to the ‘PostFailed’ event that gets called when the Post operation fails. The EventArgs of the event contains the exception information.
  • Call the ‘Post’ method on the HTTPPostHelper with the input object and the URL address to make the Post operation happen.
  • The input object gets serialized by default using the DataContractSerializer. But this can be overridden by setting the Serializer property of the HTTPPostHelper with an appropriate delagate that serializes the input object to a stream.
  • The output from the HTTP Post operation is deserialized by default using the DataContractSerializer. But this too can be overridden by the Deserializer property of the HTTPPostHelper.
  • For example, if you want to send and receive JSON data to the HTTP endpoint, just override the default Serializer & Deserializer with delegates that use DataContractJsonSerializer instead or your own custom binary serializer if need be.
  • The ‘ContentType’ used for the Post operation can also set before calling the Post method.
  • The class handles all the thread hopping internally. The ‘PostCompleted’ and the ‘PostFailed’ events get raised on the same thread from which they were called from.

 

public class HTTPPostHelper<T, K>

{

    SynchronizationContext syncContext;

    HttpWebRequest _request;

 

    public string ContentType

    {

        get;

        set;

    }

 

    public event EventHandler<EventArgs<K>> PostCompleted;

 

    public event EventHandler<ExceptionEventArgs> PostFailed;

 

    public Action<T, Stream> Serializer = null;

 

    public Func<Stream, K> Deserializer = null;

 

    public HTTPPostHelper()

    {

        Serializer = DefaultSerializer;

        Deserializer = DefaultDeserializer;

        ContentType = “application/stream”;

    }

 

    void DefaultSerializer(T input, Stream output)

    {

        DataContractSerializer serializer = new DataContractSerializer(typeof(T));

        serializer.WriteObject(output, input);

        output.Flush();

    }

 

    K DefaultDeserializer(Stream input)

    {

        DataContractSerializer serializer = new DataContractSerializer(typeof(K));

        K result = (K)serializer.ReadObject(input);

        return result;

    }

 

    public void Post(Uri address, T input)

    {

        syncContext = SynchronizationContext.Current;

 

        _request = (HttpWebRequest)WebRequest.Create(address);

        _request.Method = “POST”;

        _request.ContentType = “application/stream”;

 

        _request.BeginGetRequestStream(new AsyncCallback(PostRequestReady), new HttpWebRequestData<T>()

        {

            Request = _request,

            Data = input

        });

    }

 

    private void PostRequestReady(IAsyncResult asyncResult)

    {

        HttpWebRequestData<T> requestData = asyncResult.AsyncState as HttpWebRequestData<T>;

        HttpWebRequest request = requestData.Request;

        Stream requestStream = request.EndGetRequestStream(asyncResult);

        Serializer(requestData.Data, requestStream);

        requestStream.Close();

 

        request.BeginGetResponse(new AsyncCallback(PostResponseCallback), request);

    }

 

    private void PostResponseCallback(IAsyncResult ar)

    {

        try

        {

            HttpWebRequest request = ar.AsyncState as HttpWebRequest;

            HttpWebResponse response = request.EndGetResponse(ar) as HttpWebResponse;

            Stream responseStream = response.GetResponseStream();

            K urls = DefaultDeserializer(responseStream);

 

            syncContext.Post(PostExtractResponse, new HttpWebResponseData<K>()

            {

                Response = response,

                Data = urls

            });

        }

        catch (Exception ex)

        {

            syncContext.Post(RaiseException, ex);

        }

    }

 

    private void RaiseException(object state)

    {

        Exception ex = state as Exception;

        if (PostFailed != null)

            PostFailed(this, new ExceptionEventArgs(ex));

    }

 

    private void PostExtractResponse(object state)

    {

        HttpWebResponseData<K> responseData = state as HttpWebResponseData<K>;

        if (PostCompleted != null)

            PostCompleted(this, new EventArgs<K>(responseData.Data));

    }

}

 

public class ExceptionEventArgs : EventArgs

{

    public Exception ExceptionInfo { get; set; }

 

    public ExceptionEventArgs(Exception ex)

    {

        ExceptionInfo = ex;

    }

}

 

 

 

public class EventArgs<T> : EventArgs

{

    private T eventData;

 

    public EventArgs(T eventData)

    {

        this.eventData = eventData;

    }

 

    public T EventData

    {

        get

        {

            return eventData;

        }

    }

}

2 Responses

Subscribe to comments with RSS.

  1. dylan said, on September 30, 2009 at 12:47 am

    Interesting – I just wrote a database-based message queue using a similar approach. Obviously my goal was to persist classes using the DataContractSerializer rather than dealing with HTTP POSTs but I see a lot of similarities. Except my code is more verbose🙂 Thanks for sharing.

  2. Bill Thorp said, on November 2, 2010 at 3:52 pm

    Not to ask a stupid question, but Silverlight 3 obviates much of the code in this helper, because WebClient added UploadStringAsync() support, right?
    [http://msdn.microsoft.com/en-us/library/ms144240(v=VS.95).aspx]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: