Microsoft recently made available the long awaited Beta 1 of Silverlight Version 2.0. This new version of Silverlight includes .NET language and framework support bringing cross-platform .NET development to live. Silverlight not only comes with the first iteration of rich controls which allow developers to design great user interfaces but also contains a pretty sophisticated network stack.
In this blog post I'm going to take you through creating a Silverlight application which sends an HTTP POST request out to a (very basic PHP driven) service and prints out the response. While Silverlight implements higher level abstractions for accessing SOAP and REST based services I've seen numerous people asking for HTTP POST request support with parameters transmitted as part of the HTTP request as opposed to URL encoding them (which would be an HTTP GET request then). The popularity might be driven by users who are learning Silverlight programming and simply want to start by replacing traditional HTML forms with a Silverlight front end. Another driver might be the mere existence of "services" which accept HTTP POST requests and have not yet been migrated to a full blown REST model.
I assume you've got a solid understanding of .NET and know how to handle Visual Studio 2008. I also assume you've successfully installed all the bits required to build Silverlight 2.0 Beta 1 apps. (If not, Tim Sneath has a great post outlining what's needed.)
In order for you to easily follow along I've set up a very simple service which accepts two HTTP POST parameters (lastname and firstname) and returns a simple XML structure which - if you successfully submitted HTTP POST parameters - feeds back the input parameters. If not, it replaces them with "null".
The service can be freely accessed at http://www.24100.net/labs/silverservice.php. The root of my web server also services a clientaccesspolicy.xml file which allows any Silverlight application out there to access the service. (See the MSDN article How To Make A Service Available Across Domain Boundaries for details.)
Let's get started.
Fire up Visual Studio 2008 and create a new Silverlight Application. I usually create a Web Application along with it just because it makes testing more easy with the built-in development web server.
In the main application page Page.xaml add a button, name it Execute and hook up an event handler to the Click event:
Next add a TextBlock named Log which we are going to use to output debug information. I've arranged everything in a vertical StackPanel to make it look a bit better.
This is actually all we are going to do on the user interface side. The rest of the tutorial will focus on coding the HTTP POST request and happen in Page.xaml's code behind file Page.xaml.cs.
Open the code behind file Page.xaml.cs. If you've used Visual Studio's IntelliSense to create the Click event before, you'll find the Execute_Click() handler method stub prepared for you:
The HttpWebRequest class we are going to use resides in the System.Net namespace which does not get added to your Silverlight project by default Right-click on References > Add Reference... and manually add the System.Net assembly...
... and import the namespace by adding the appropriate Using statement at the top of Page.xaml.cs:
Now we are all set to start coding. The first thing we are going to do is creating an instance of System.Uri. We are then going to use the static Create() method of System.Net.WebRequest to factor an instance of type HttpWebRequest:
If you've used HttpWebRequest in traditional .NET applications before, you'll find out that the Silverlight implementation does not provide a synchronous GetResponse() member. Instead we do have to use .NET's asynchronous programming model to proceed. This is, where things get a bit more complicated. Before we actually proceed with sending out the HTTP request, we have to set the method - which is POST - and set the way the data gets encoded:
As stated above HttpWebRequest offers an asynchronous way to handle HTTP requests and responses. We are starting the request by calling the BeginGetRequestStream() method of our request object. The method expects two parameters: The first one is a callback method of type System.AsyncCallback which gets called once the asynchronous creation of the request stream completed successfully. The second parameter can be any object and represents a state object. Why do we need the second parameter anyway? The problem with our asynchronous operation is that the code inside our asynchronous callback method does not necessarily have access to objects inside the "calling" method. The object provided as the second parameter of BeginGetRequestStream() is available inside the asynchronous callback method via the AsyncState property. If the asynchronous callback method would not require any object from within the "calling scope", it would be perfectly legal to use null as the second parameter.
With that said we are going to submit the our request object itself as the second parameter so we can access it's members within the asynchronous callback method.
Here is the code:
Note that RequestProceed() is still underlined because we have not yet implemented it. Just for clarity: We are executing the asynchronous BeginGetRequestStream() method on our HttpWebRequest instance stating that once the operation has completed, we want to proceed with the RequestProceed() method and hand over our HttpWebRequest instance itself via the AsyncState mechanism.
Please be also aware that we are starting and asynchronous request here which will allow us to write (aka POST) data along with it. We are not handling a response, yet!
To get rid of Visual Studio's complain about the non existing RequestProceed() method let's go ahead and create the asynchronous callback method:
RequestProceed() must implement AsyncCallback's signature.
In the next step we first want to get back a reference to our initial request object from the calling scope. Remember that we submitted the initial request as the AsyncState parameter of BeginGetRequestStream(). All we need to do is cast it from IAsyncResult's AsyncState property:
Now we do need a StreamWriter in order to actually write to the request stream. StreamWriter is part of the System.IO namespace so we got to add a Using statement to our project:
StreamWriter's constructor expects a Stream object. We do get this via EndGetRequestStream() which itself expects an object which implements IAsyncResult and represents a pending request for a stream. Thanks god, we got both at hand! In the next line of code we are going to new up a StreamWriter object by calling our request's EndGetRequestStream() method and passing in the IAsyncResult object reference from our "calling scope":
We are almost there! With this brand new StreamWriter we can now easily write to the HTTP request stream.
POST parameters are defined in a format which you might very well know from URL encoded parameters. We want to hand in a firstname and a lastname parameter. Here we go:
Please do note that we are calling the Close() method which sends the data over the wire and closes the underlying stream. There also is a Flush() method available which sends the data out but does not close the stream. Just bear in mind that at some stage you should actively close any stream to release resources.
At this point we have send out our HTTP POST request to http://www.24100.net/labs/silverservice.php and transmitted two parameters with it. What's left to do is handling the response. Once you've followed along until here, it's an easy one as it again involves asynchronous callbacks.
So the last action inside our request callback method is to kick off handling the response:
You recognize the pattern. We are starting the asynchronous response handling by calling the BeginGetResponse() method of our request object. We are defining the ResponseProceed() method to be executed once the asynchronous call completed successfully and again "forward" our initial request via the AsyncState mechanism.
In our asynchronous response handler the first step we do is again casting the initial request via the asyncResult.AsyncState property. We then get an HttpWebResponse instance by calling the requests EndGetResponse() method. Finally we new up a StreamReader for the Stream returned by HttpWebResponse's GetResponseStream() method. Here is the straight forward code:
The last and final step is to actually use StreamReader to read the response from the web server:
Finally we assign the response to our Silverlight TextBlock control:
Here is the browser output after clicking the Execute button:
(You can try it out here if you've got Silverlight 2.0 installed!)
This is all we've got to do. In my next post I'm going to guide you through encapsulating all the asynchronous plumbing above into a separate class and using Silverlight's powerful eventing mechanisms to feed back responses to the main UI thread. The service at http://www.24100.net/labs/silverservice.php is available for you to test. Feel free!