Udostępnij za pośrednictwem


Clients and the Automatic Help Page in WCF WebHttp Services

This is part two of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Using the HttpClient from the WCF REST Starter Kit Preview 2
  • Browsing the Automatic Help Page of a WCF WebHttp Service

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:   If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Creating the TeamTask.Client Project

At the end of part one of the this blog series we had created the TeamTask service and exposed two HTTP GET operations: one for retrieving a list of tasks and another for retrieving a single user.

We’ll now add a simple console project to our solution to use for our client.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. In the “Solution Explorer” window (Ctrl + W, S), right click on the TeamTask solution and select “Add”—>”New Project”. This will open the “New Project” dialog window.

  3. On the left-hand side of the “New Project” dialog window select “Installed Templates” and then select “Visual C#”—>”Windows” from the tree-view control.  Choose the “Console Application” project template.

  4. For the project name enter “TeamTask.Client” and then click “Ok”.  This will create the TeamTask.Client project in the solution.

Step 2: Adding the HttpClient to the TeamTask.Client Project

There are numerous ways to consume a WCF WebHttp Service like the TeamTask service.  You could use the WCF WebChannelFactory, the HttpWebRequest class, or even the WebClient class.  However, we recommend using the HttpClient.

The HttpClient is part of a client-side library that was included with the WCF REST Starter Kit Preview 2.  In order to use the HttpClient we’ll download the WCF REST Starter Kit Preview 2 and include the Microsoft.Http.dll and Microsoft.Http.Extensions.dll libraries in our client project.

We’ll also need to change the target framework for the TeamTask.Client project because the default for console applications is “.NET Framework 4 Client Profile” but the HttpClient has dependencies on assemblies in the full .NET 4 profile.

  1. In the browser of your choice, navigate to WCF REST Starter Kit Preview 2 download page.

  2. Click on the WCF REST Starter Kit Preview 2.msi download link and follow the prompts to install the WCF REST Starter Kit Preview 2.

  3. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “Browse” tab and select the Microsoft.Http.dll and Microsoft.Http.Extensions.dll libraries under:

        %ProgramFiles%\Microsoft WCF REST\WCF REST Starter Kit Preview 2\Assemblies

  4. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Properties”.  On the “Applications” tab of the “Properties” editor, change the target framework from “.NET Framework 4 Client Profile” to “.NET Framework 4”.

Helpful Tip: If you would like to learn more about the HttpClient, there is a useful two-part screencast on Channel 9: part 1 & part 2.

 

Step 3: Writing Tasks to the Console

To demonstrate how easy it is to use the HttpClient to get data from our TeamTask service, we’ll retrieve a list of tasks from the service and write out the list to the console.  Later, we’ll see more advance methods for handling request and response content with the HttpClient.

  1. Open the Program.cs file from the TeamTask.Client project in the code editor and copy the code below into the Main() method.  You’ll also need to add “using Microsoft.Http;” to the code file.

        using (HttpClient client = new HttpClient("https://localhost:8080/TeamTask/")) 
        { 
            // Getting the response as a string
            Console.WriteLine("The tasks owned by user3:"); 
            using(HttpResponseMessage response = client.Get("Tasks?owner=user3")) 
            { 
                response.EnsureStatusIsSuccessful(); 
                Console.WriteLine(response.Content.ReadAsString()); 
            } 
            Console.ReadKey(); 
         }

    The HttpClient has a constructor overload that takes a base address.  This allows you to use relative URIs with all of the requests made with that given instance of the HttpClient.  This can be very convenient when you’ll only be making requests to a single domain.

    Also, the Microsoft.Http.Extensions.dll contains extension methods for the common HTTP methods GET, PUT, POST, DELETE and HEAD.  These extension methods use the more general Send() method of the HttpClient.  The Get() method used in the code above is one of these extension methods. 

    Lastly, the HttpClient does not throw an exception for non-successful requests (HTTP status codes other than 2xx).  If you want to throw an exception if the request was not successful, you need to call the EnsureStatusIsSuccessful() method.

  2. Start without debugging (Ctrl+F5) to start the TeamTask service.  Note: If you have build errors because the “Microsoft.Http” namespace doesn’t exist, make sure you have updated the TeamTask.Client project target framework version to use the full profile as explained in Step 2 above.

  3. Start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    TasksForUser3InConsole

  4. While this output is correct, the lack of formatting makes the XML difficult to read.  For XML content, a simple trick you can use to get better formatting is to use the ReadAsXElement() extension method for the HttpContent class.  Add “using System.Xml.Linq;” to the code file and then change the call to ReadAsString() like so:

        Console.WriteLine(response.Content.ReadAsXElement().ToString());

  5. Again, start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:  
    TasksForUser3InConsoleFormatted

 

Step 4: Iterating over the Tasks Using LINQ to XML

In the previous step, we used the ReadAsXElement() extension method to easily format our XML content.  However, working with the response content as an XElement tree allows us to do much more.  We’ll use the ReadAsXElement() extension method and LINQ to write just the task titles to the console.

  1. Copy the following code into the Main() method within the HttpClient “using” block:

        // Getting the titles using LINQ to XML
        Console.WriteLine("The titles of the current tasks:"); 
        XNamespace ns =
            "https://schemas.datacontract.org/2004/07/TeamTask.Model"; 
        using(HttpResponseMessage response = client.Get("Tasks")) 
        { 
            response.EnsureStatusIsSuccessful(); 
            XElement tasks = response.Content.ReadAsXElement(); 
            var taskTitles = tasks.Descendants(ns + "Title") 
                                          .Select(element => element.Value); 
            Console.WriteLine(string.Join(", ", taskTitles)); 
        } 
        Console.ReadKey();

    If you are familiar with the LINQ to XML APIs, then the above code should be easy to understand.  The only thing to be aware of is that the XML returned from the TeamTask service will have a default XML namespace, and in order to use the Descendants() method to filter all but the “Title” elements, the XNamespace has to be supplied.

  2. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:TaskTitlesInConsole

Step 5: Retrieving Tasks as Task Instances

Working with the response content as XML is useful, but being able to work with response content as strongly-typed CLR instances would be even better.  Of course, to work with the response content as strongly-typed instances, you would need to have the relevant class definitions available to your client project. 

For the sake of simplicity, we’ll simply add a reference to the TeamTask.Service project from our TeamTask.Client project in order to use the Task class.  However, referencing the service assembly like this might not always be an option. 

There are a couple of methods you could use to create classes for the HTTP message content.  The most obvious method would be to hand-code the classes, but this is a painful and error-prone process.  It would be preferable to generate the classes from a schema using a tool such as xsd.exe.  As we’ll see in step 6 below, WCF WebHttp Services provides an automatic help page that makes it easy to distribute such schema for the types used in your service.

  1. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “Projects” tab and choose the TeamTask.Service project.

  2. Open the Program.cs file in the code editor if it isn’t already open.

  3. Add “using TeamTask.Model;” to the code file.

  4. We’ll use the ReadAsDataContract() extension method in the Microsoft.Http.Extensions.dll, but it has a dependency on the System.Runtime.Serialization assembly, which we need to add to our project.  In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “.NET” tab and choose the System.Runtime.Serialization assembly. 

  5. Add “using System.Runtime.Serialization;” to the Program.cs file.

  6. Copy the following code into the Main() method within the HttpClient “using” block:

        // Getting the response as a strongly-typed List<Task>
        Console.WriteLine("The task owners:");
        using(HttpResponseMessage response = client.Get("Tasks"))
        {
            response.EnsureStatusIsSuccessful();
            List<Task> tasks = response.Content.ReadAsDataContract<List<Task>>();
            var taskOwners = tasks.Select(task => task.OwnerUserName);
            Console.WriteLine(string.Join(", ", taskOwners));
        }
        Console.ReadKey();

  7. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    TaskOwnersInConsole_sansUserNames

Helpful Tip: The WCF REST Starter Kit Preview 2 includes a useful Visual Studio 2008 Add-in called Paste as Xml Types that makes generating types from an XML schema as easy as copying and pasting.  Unfortunately, the add-in will not work in Visual Studio 2010.  But look for a similar Add-in for Visual Studio 2010 on the Visual Studio Gallery in the future.

 

Step 6: Using the Automatic Help Page in WCF WebHttp Services

Writing the client code like we have been in this blog post so far has been easy because we wrote the TeamTask service and we know what the valid URIs for the service look like and what kind of response content we should receive.  But of course this isn’t always the case. 

If your are developing a client for a web service like TeamTask service and you didn’t implement the service, how are you supposed to know how to use the service?  Likewise, if you are developing a web service like the TeamTask service but are allowing other developers to implement clients for the service, how are you supposed to communicate how to use the service? 

The answer is the new automatic help page that is provided with WCF WebHttp Services in .NET 4.  The best way to get a sense of the automatic help page is to see it for yourself.

  1. Start without debugging (Ctrl+F5) to start the TeamTask service.

  2. When the automatic help page is enabled (it is enabled by default with the online project template), it can be found at the URI “/help” relative to your service’s base address.  So in the browser of your choice, navigate to https://localhost:8080/TeamTask/help.  In Internet Explorer, the help page will be displayed as shown below:

    OperationsHelpPageInBrowser 

    The automatic help page provides a list of the valid URIs, the HTTP methods that can be used with a given URI and a default description of the operation.  We’ll see how to set the descriptions to something more useful in the next step.

  3. Notice that the HTTP methods listed in the automatic help page happen to be hyperlinks.  Follow the GET hyperlink for the “Tasks” URI.  In Internet Explorer, the following page will be displayed:

    UriHelpPageInBrowser

    This page offers a lot of useful information about the given operation, including the URI, HTTP method, the request and response formats (XML and JSON), examples in both XML and JSON, and even the XML schema.  This page provides client developers with all of the information they need to be successful.

Step 7: Providing Descriptions for the TeamTask Operations

As we saw in step six, the main help page at the relative  “/help” URI provides a list of operations that the service exposes.  For each of theses operations, there is a description field, which is just the absolute URI for the operation by default.  Providing client developers with more useful descriptions is as easy as adding [Description] attributes to the operations of the service.

  1. Open the TeamTaskService.cs file in the code editor if it isn’t already open.

  2. Add “using System.ComponentModel;” to the code file.

  3. Add a [Description] attribute to the GetTasks() method like so:

        [Description("Returns the tasks that are owned by the team.")] 
        [WebGet(UriTemplate = "Tasks?skip={skip}&top={top}&owner={userName}")] 
        public List<Task> GetTasks(int skip, int top, string userName)

  4. Add a [Description] attribute to the GetUser() method like so:

        [Description("Returns the details of a user on the team.")] 
        [WebGet(UriTemplate = "Users/{userName}")] 
        public User GetUser(string userName)

  5. Start without debugging (Ctrl+F5) to start the TeamTask service.  In the browser of your choice, navigate to https://localhost:8080/TeamTask/help.  In Internet Explorer, the help page will be displayed as shown below:

    HelpPageWithDescriptionsInBrowser

    Notice that the description fields for the operations now reflect the values from the operation attributes.  Note:   You may need to stop the ASP.NET development web server and then restart the TeamTask service in order to see the changes to the help page.  To stop the development server, right-click on the notification icon in the notification area of the Windows Taskbar and select "Stop" from the context menu that appears.  

Next Steps: Updating State in WCF WebHttp Services

We’ve got our TeamTask service up and running and we’ve also created a client that can retrieve data from the TeamTask service.  However, with a service like the TeamTask service, static server state isn’t very interesting.  In part three of this blog post series, we’ll add a new operation to the TeamTask service that will allow clients to update tasks.

Randall Tombaugh
Developer, WCF WebHttp Services

Post2-ClientAndHelpPage.zip

Comments

  • Anonymous
    January 12, 2010
    Good stuff, thanks Randall!

  • Anonymous
    February 07, 2010
    I'm having trouble with the help page. It loads just fine and shows my descriptions. I can't follow any of the method urls though. The service just hangs until it eventually times out. Is it normal the urls are like this? http://localhost:50363/MyService/help/operations/GetEntity Any ideas?

  • Anonymous
    February 09, 2010
    @kareem613:  My best guess is that you changed something on the server so that you are getting a runtime exception when opening the host and you are only seeing the help page because it is being cached.   The url you give does look correct--it will be of the form: <baseAddressOfService>/help/operations/<operationName>. Let me know if this still isn't working for you and if you post the code somewhere I'll take a look.

  • Anonymous
    February 12, 2010
    I've narrowed it down. It works just fine on two dev machines. Both 32bit platforms. The issue only shows when I deploy to a 64bit windows 2008 server. I've remoted onto the server and can see the cpu usage for the w3wp process spike straight to 100% and hold indefinitely. I've tried both option for enabling 32 bit applications. Could this be related to wcf/rest? Have you tried deploying to a 64bit win2k8 server?

  • Anonymous
    February 12, 2010
    More clues. The first request for method details seems to work fine sometimes. Then its the second that triggers it. I got a memory dump and ran it through debugdiag and the thread eating up all the cpu has a bunch of calls in the System.ServiceModel.Web namespace.

  • Anonymous
    February 22, 2010
    @ kareem613 Actually, I developed the code for this blog post series on a 64bit win2K8 server and I didn't have any problems.  Would you be able to post your code somewhere?

  • Anonymous
    March 30, 2010
    Can you explain how I can use the xsd.exe and the schema to generate client proxy classes. Thanks Scott

  • Anonymous
    March 30, 2010
    Am I right in thinking that the help page is only going to show Http GET Methods?

  • Anonymous
    March 30, 2010
    Will WCF WebHttp Services allow a client proxy to be generated using Visual Studio Add Service Reference? If not why, seeing how WCF Data Services, WCF SOAP Serivces and WCF RIA Services all allow it? Thanks Scott