Windows 8 and ASP.NET Web API - Part 1 - Getting Started
Many of the key Windows 8 Metro Style application features either require or can greatly benefit from a connection to online services. ASP.NET Web API is a great fit for these services, as it's tightly focused around providing HTTP services to applications. I'm starting a series that will look at how to leverage ASP.NET Web API services from Windows 8 applications.
In this first post in the series, we'll begin with a simple C# Windows 8 Metro Style application that displays data from the ASP.NET Web API Contact Manager sample. Once we've got the basics locked down, we'll move on to some more complex topics. I'm thinking Live Tiles and Notifications, but I'm interested in your ideas of where to go next, so please let me know in the comments.
The ASP.NET Web API Contact Manager sample
I thought it would be simplest to start with an official, tried and true Web API sample that's been around since the early days of WCF Web API - the Contact Manager Sample. You can download the code here: http://code.msdn.microsoft.com/Contact-Manager-Web-API-0e8e373d
This code is originally written for Visual Studio 2010, but I'm going to be working on Visual Studio 11 Beta running on Windows 8 Consumer Preview, so I ran through the upgrade. There's an alert which says "One or more Web Application projects in this solution require SQL Server Express. You will need to download and install the appropriate version of SQL Server Express for these projects to work." A quick look at the code, however shows that all the data is in-memory, so we can ignore it. If we keep with this code for long, we can just remove the offending NuGet package, but let's not let that slow us down just yet.
Running the application shows that the application is indeed working.
The HTML client page is making an .getJSON() call back to the Web API service, which returns a list of contacts in JSON format, then uses jQuery templating to display the contacts in HTML format. That jQuery templating should probably be updated to use the newer JSRender system, but it doesn't really matter for this example, since we're just concerned about the services, not the HTML client.
Looking at the ASP.NET Web API's services using F12 developer tools
We're going to be using Fiddler2 in a bit, but since the simplest way to view the output of the Web API's JSON services is in a browser. In IE, you do that by pressing the F12 hotkey to display developer tools - it looks like this:
Now switch to the Network view (or your browser's equivalent), start monitoring, and browse to /contacts (in my case the full URL is http://localhost:33936/contacts, your port number will vary). IE prompts you to download the JSON file and the browser's address bar goes back to the previous. That's a little wacky - other browsers display the JSON as text - but the service call response is included in the results. Clicking on it to bring up the detailed view and showing the response body shows the JSON data:
The JSON data returned is as follows (I removed some of the contacts from the list for brevity):
[ { "Address" : "1 Microsoft Way", "City" : "Redmond", "ContactId" : 1, "Email" : "example@microsoft.com", "Name" : "Glenn Block", "Self" : "contact/1", "State" : "Washington", "Twitter" : "gblock", "Zip" : "98112" }, { "Address" : "1 Microsoft Way", "City" : "Redmond", "ContactId" : 2, "Email" : "example@microsoft.com", "Name" : "Howard Dierking", "Self" : "contact/2", "State" : "Washington", "Twitter" : "howard_dierking", "Zip" : "98112" }, { "Address" : "1 Microsoft Way", "City" : "Redmond", "ContactId" : 3, "Email" : "example@microsoft.com", "Name" : "Yavor Georgiev", "Self" : "contact/3", "State" : "Washington", "Twitter" : "digthepony", "Zip" : "98112" }]
That content's being returned from the ContactsController's Get action, which just returns a Queryable list:
public IQueryableGet() { return this.repository.GetAll().AsQueryable(); }
Out of the box, ASP.NET Web API can format data in JSON and XML format, so the output is automatic depending on content negotiation. If you're familiar with ASP.NET Web API, this is all pretty standard stuff. If you're not, I recommend the ASP.NET Web API content section at http://asp.net/web-api and my previous blog / screencast series on getting started with ASP.NET Web API.
The point is that this sample is producing JSON data that we can display in our Windows 8 Metro application.
Creating a Windows 8 Metro style application using the Blank template
To create the Windows 8 Metro application, add a new project using the File / Add / New Project... dialog. Select the Visual C# / Windows Metro Style section, then pick the Blank Application template. I called mine ContactManagerMetro because I have no imagination.
Why the Blank Application template? Good question. I started with the Split Application template, but it has existing data structures which are hierarchical (since the Split Application helps with navigating structured content), so it wasn't a good fit. I think the Blank Application template is a good start since it's nice and simple.
This application is, as promised, rather blank. The main form is even called BlankPage.xaml, which while being rather descriptive of the initial state, seems unlikely to remain accurate for very long in most cases. Since we're just proving concepts at this point, though, I'm going to grit my teeth and leave the name as BlankPage.xaml. Focus.
Writing the Metro Application XAML
We've got a lot of options for how to display the contact information, but I think a good start is using the same kind of display that the original HTML view used. In the Metro XAML land, that's usually done using a ListView. Inside BlankPage.xaml's empty grid, I'm going to add the following ListView markup:
<GridView x:Name="ContactList"> <GridView.ItemTemplate> <DataTemplate> <StackPanel Width="200" Height="150" Margin="10" Background="#FF161C8F"> <TextBlock Text="{Binding Name}" FontSize="24" /> <TextBlock Text="{Binding Address}" /> <TextBlock Text="{Binding City}" /> <TextBlock Text="{Binding State}" /> <TextBlock Text="{Binding Zip}" /> <TextBlock Text="{Binding Twitter}" /> </StackPanel> </DataTemplate> </GridView.ItemTemplate> </GridView>
This is pretty rudimentary, we'll clean it up later. The main reason I'm writing it now is to make sure I know what I'll be binding to, and for the above TextBlock binding values I just based the values off the contact properties returned by the service.
Calling the Web API service from our Metro Application
Calling the service is actually pretty straightforward. First, I'll use the System.Net.Http.HttpClient to call the service. As all external calls in WinRT are async, I need to handle that. Fortunately the async/await keywords make that really easy.
Since our service returns JSON, I'm going to leverage the utility classes in the Windows.Data.Json namespace to parse it. The code is a little repetitive and could definitely use some refactoring, but it handles the job of mapping JSON values to an anonymous object. Tim Heuer recently blogged about using that with anonymous type binding, and I liked his approach.
With all the plumbing in place, I'll call this GetContacts() method from my OnNavigatedTo event.
protected override void OnNavigatedTo(NavigationEventArgs e) { GetContacts(); } public async void GetContacts() { var serviceuri = "http://localhost:33936/contacts"; var client = new System.Net.Http.HttpClient(); var response = await client.GetAsync(serviceuri); if (response.IsSuccessStatusCode) { var data = await response.Content.ReadAsStringAsync(); var contacts = JsonArray.Parse(data); var qry = from m in contacts select new { Name = m.GetObject()["Name"].GetString(), Address = m.GetObject()["Address"].GetString(), City = m.GetObject()["City"].GetString(), State = m.GetObject()["State"].GetString(), Zip = m.GetObject()["Zip"].GetString(), Twitter = m.GetObject()["Twitter"].GetString(), }; ContactList.ItemsSource = qry; } }
This calls into our service and displays the following data:
Hurrah! We got data from our service and viewed it in the app. It's pretty ugly, though. Let's clean up the formatting a bit so it matches the original app a little better. I'm using the underappreciated <Run> XAML element for that:
<GridView x:Name="ContactList"> <GridView.ItemTemplate> <DataTemplate> <StackPanel Width="250" Height="150" Margin="10" Background="#FF161C8F"> <TextBlock Text="{Binding Name}" FontSize="28" Margin="10,20,10,0" /> <TextBlock Margin="10,0"> <Run Text="{Binding Address}" />, <Run Text="{Binding City}" /> </TextBlock> <TextBlock Margin="10,0"> <Run Text="{Binding State}" /> <Run Text="{Binding Zip}" /> </TextBlock> <TextBlock Margin="10,0"> @<Run Text="{Binding Twitter}" /> </TextBlock> </StackPanel> </DataTemplate> </GridView.ItemTemplate> </GridView>
That at least organizes the text a bit:
And that's at least a decent start. Questions? Where should we go next?