Conditional GET and ETag Support in WCF WebHttp Services

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



  • Generating and including ETags in responses from a WCF WebHttp Service 

  • Adding Conditional GET support to a WCF WebHttp Service using ETags

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: Adding ETags to Responses


Entity Tags (or ETags) are a crucial aspect of the caching mechanisms in HTTP.  An ETag is an opaque quoted string that may be returned along with a response in an ETag HTTP header.  ETags are used to indicate whether or not a resource has changed state.  If two requests for the same resource result in responses with the same ETag then it can be assumed that the state of the resource has not changed between the two requests.


As we’ll see, ETags can be used to implement conditional GET.  With conditional GET the client sends the ETag from a previous response and effectively asks the server to only send the content if the resource state has changed.  Using conditional GET reduces the bandwidth demands on the service and client since the content need not be sent if the client already has the most recent version.


To demonstrate the ETag support in WCF WebHttp Services for .NET 4 we’ll update the TeamTask service to send responses with ETags for all requests of a single task.  The GetTask() service operation on the TaskService class is responsible for returning individual tasks, so it’s the operation we’ll be modifying. 


We’ll need to do two things in order to provide ETags with responses from the GetTask() operation.  First, we’ll need to specify that a given ETag should be included with a given response.  As you’ll see below, this is easily done by calling the new SetETag() method from the WebOperationContext within the service operation.   But we also need to generate the ETag in the first place.  Reliably and efficiently generating Etags for our tasks could be a messy job, but since we’re using an SQL database to store our tasks, we can delegate the ETag generation to the database and make it easy for ourselves.


We’ll be using the Version property of the Task type for our ETags.  The Version property has existed on the Task type from the very first post of this series but you might not have noticed it since we haven’t been serializing it when generating responses.  The Version property on the Task type is mapped to the Version column of the Tasks table in the TeamTask.mdf database file.  If you open the Tasks table definition in the Server Explorer window (Ctrl+W, L), you’ll see that the Version column is a timestamp data type that is updated by SQL anytime the task is updated in the database.


The Version property of the Task type is an eight byte array, so the first thing we’ll do is create a an extension method that will encode the eight byte array as a string representing a hexadecimal number.




  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 (Ctrl+W, S) right click on the TeamTask.Service project and select “Add”->”Class” from the context menu that appears.  In the Add New Item window enter the name “Extensions.cs” and hit enter.



  3. Make the Extensions class static and add the following ToHexString() extension method as shown below.  You’ll also need to add “using System.Linq;” to the code file:


        public static class Extensions
       
    {
            public static string ToHexString(this byte[] byteArray)
            {
                return string.Join(string.Empty, byteArray.Select(b => b.ToString(“X”)));
            }
        }



  4. Now open the TaskService.cs file in the code editor and add the following two lines of code to the GetTask() operation directly before returning the task within the using code block:


        string etag = task.Version.ToHexString();
        WebOperationContext.Current.OutgoingResponse.SetETag(etag);


    By calling the SetETag() method for the outgoing response we ensure that an HTTP ETag header with the task version value is included with the response.



  5. The complete  GetTask() operation should look like the following:


        [Description(“Returns the details of a single task.”)]
        [WebGet(UriTemplate = “{id}”)]
        public Task GetTask(string id)
        {
            int parsedId = ParseTaskId(id);
            using (TeamTaskObjectContext objectContext =
                          new TeamTaskObjectContext())
            {
                var task = objectContext.Tasks 
                                          .FirstOrDefault(t => t.Id == parsedId);
                if (task == null)
                {
                    ThrowNoSuchTaskId(parsedId);
                }

                string etag = task.Version.ToHexString();
                WebOperationContext.Current.OutgoingResponse.SetETag(etag);
                    
                return task;
            }
        }






Helpful Tip:  The SetETag() method is found on the OutgoingResponse of the WebOperationContext, which also provides access to the response headers and even an ETag property.  However, the SetETag() method will ensure that the ETag is correctly quoted as per the HTTP specification so you should use it instead of the headers collection or the ETag property.


 


Step 2: Adding Conditional GET Support


In step one of this blog post we briefly discusses the concept of a conditional GET.  If a client has already sent a previous request for a resource and received a response along with an ETag, the client can send that ETag in an HTTP If-None-Match header on future requests.  If the state of the resource hasn’t changed since the previous request, the server can send back a response without a body and an HTTP status code of 304 (Not Modified).  Of course, if the state of the resource has changed since the previous request, the server will respond with the new state of the resource and a new ETag. 


In the next step, we’ll update our client to use ETags and send conditional GET requests so we’ll see conditional GET in action.  But first we need to edit the GetTasks() operation of the TaskService service class so that it checks for the HTTP If-None-Match header.  We’ll add this check for the If-None-Match header with a single call to the new CheckConditionalRetrieve() method from the WebOperationContext. 


The CheckConditionalRetrieve() method takes the current ETag as an argument and tries to match it against the ETags in the If-None-Match header of the request.  If there is a match then the client already has the current version of the resource so the CheckConditionalRetrieve() method throws a WebFaultException with an HTTP status code of 304 (Not Modified) just as the HTTP specification prescribes.  If there is no If-None-Match header in the request or if none of the ETags in the header match the current ETag, then the CheckConditionalRetrieve() method returns so that service operation can complete as normal. 




  1. Now open the TaskService.cs file in the code editor and add a call to the CheckConditionalRetrieve() method in the GetTask() operation like so:


        string etag = task.Version.ToHexString();
        WebOperationContext.Current.IncomingRequest
            .CheckConditionalRetrieve(etag);
        WebOperationContext.Current.OutgoingResponse.SetETag(etag);






Helpful Tip:  In part seven of this blog post series we discussed how ASP.NET server and client-side caching functionality can be leveraged by a WCF WebHttp service in .NET 4.  It’s important to consider how ETags and conditional GET interacts with server and client-side caching.  


For example, conditional GET support requires checking the ETag from the request  within the service operation.  However, if server-side caching has naively been enabled, two requests with different ETags (one current, the other stale) may both receive the same response from the ASP.NET output cache.  The correct way to compose server-side caching and conditional GET is to cache different responses based on the value of the HTTP If-Modified-Since header from the requests.  Therefore when a request with a new ETag value is received there will be a cache-miss and the service operation will execute.   In step two of part seven of this blog post series we discussed how to cache based on request headers. 


The right caching mechanisms for your service will depend on the type of resources your service exposes.  If a resource changes rarely (or at known intervals) and you expect lots of different clients to only request the resource once, then server-side caching makes sense because you can serve those requests from the cache.  If you expect clients to poll an often-changing resource for updates, then you’d probably want to include conditional GET support.    


 


Step 3: Updating the Client to use ETags


Now that we’ve implemented the GetTask() service operation to both return ETags and support conditional GET requests, let’s update the client to take advantage of this new functionality.


We’ll first have the client send a GET request for a task.  The client won’t yet have an ETag for the task, so it won’t include one.  However, we’ll capture the ETag from the response and send a second request with the same ETag in an If-Modified-Since header.  With this second request we’ll see that the TeamTask service will return a 304 (Not Modified) response and no message body.  We’ll then update the task and send a third request with the original ETag.  Since we’ve updated the task, the original ETag is stale and the response to the third request will include the updated task and a new ETag.




  1. Open the Program.cs file of the TeamTask.Client project in the code editor and replace the current implementation of the static GetTask() method with the implementation below.  You’ll also need to add “using Microsoft.Http.Headers;” and  “using System.Net;” to the code file:


        static Task GetTask(HttpClient client, int id, ref EntityTag etag)
        {
            Console.WriteLine(“Getting task ‘{0}’:”, id);
            using (HttpRequestMessage request =
                          new HttpRequestMessage(“GET”, “Tasks/” + id))
            {
                request.Headers.Accept.AddString(“application/json”);
                if (etag != null)
                {
                    request.Headers.IfNoneMatch.Add(etag);
                }
                using (HttpResponseMessage response = client.Send(request))
                {                
                    if (response.StatusCode == HttpStatusCode.NotModified)
                    {
                        Console.WriteLine(
                            “The task with id ‘{0}’ has not been modified.”, id);
                        Console.WriteLine();
                        return null;
                    }
                    else
                   
    {
                        etag = response.Headers.ETag;
                        response.EnsureStatusIsSuccessful();
                        Console.WriteLine(“Etag: {0}”, etag.Tag);
                        WriteOutContent(response.Content);
                        return response.Content.ReadAsJsonDataContract<Task>();
                    }
                }
            }
        }


    This new implementation of the GetTask() method is similar to the client code that we created in part two of this blog post series except that we’ve added the conditional GET functionality.  Before sending the request, we include the If-None-Match header by calling the IfNoneMatch.Add() method on the headers collection.  After receiving the response we check the status code for a value of 304 (Not Modified).



  2. Replace the Main() method implementation with the following code:


        using (HttpClient client = new HttpClient(“http://localhost:8080/TeamTask/”))
        {
            EntityTag etag = null;
                    
            // Get the task and the etag
          
    Task task = GetTask(client, 2, ref etag);

            // Get the task a second time and include the etag
           
    GetTask(client, 2, ref etag);

            // Update the task;
           
    task.Status = (task.Status == TaskStatus.Completed) ?
                TaskStatus.InProgress :
                TaskStatus.Completed;
            UpdateTask(client, task);

            // Get the task again… the etag should be stale
           
    GetTask(client, 2, ref etag);

            Console.ReadKey();
        }



  3. 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:



    Notice that the second response for task ‘2’ has a status code of 304 (Not Modified) and that the ETag returned for the third request is different than the ETag returned from the first request because of the update that occurred between the two requests.


Next Steps: Optimistic Concurrency Support in WCF WebHttp Services


In this part of the series we’ve shown how easy it is to add ETag and conditional GET functionality to a WCF WebHttp service in .NET 4.  In part eleven of this blog post series we’ll explore how ETags can be used to implement optimistic concurrency in a WCF WebHttp service in order to prevent clients from unknowingly updating data that has changed since they last retrieved it.  


Randall Tombaugh
Developer, WCF WebHttp Services

ESB Toolkit 2.0 Portal 401 Exception

I had this exceptionin the Application Event Log after trying to bring up the portal in IE (after a newly installed and configured BizTalk & ESB platform):

Exception information:
Exception type: WebException
Exception message: The remote server returned an error: (401) Unauthorized.

I was working in atwo (application) server BizTalk environment but Ionly had this exception on one server. The portal came up fine on the other server.

I looked at IIS log files and found a 401 for /ESB.Exceptions.Service/ExceptionService.svc/GetUserSettings

I browsed to the /ESB.Exceptions.Service/ExceptionService.svc in IE and saw this in the Application Event Log:

Exception information:

Exception type: NotSupportedException

Exception message: Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.

Uhh… I did have that configured:Windows Integrated was turned on and Anonymous was turned off.

The issue was that I did not run the set command for NTAuthenticationProviders (specified in this KB: http://support.microsoft.com/kb/215383)on that server. I had previously ran the set command on the other server in my environment before running the Microsoft.Practices.ESB.UDDIPublisher.exe

Host Integration Server generates a lot of warning in the eventlog

The following warning is often seen on servers running Microsoft Host Integration Server:

Log Name:      Application
Source:        SNA Base Service
Date:          date
Event ID:      561
Task Category: None
Level:         Warning
Keywords:      Classic
User:          user
Computer:      computer
Description:
Write to mailslot or socket failed, rc = 64

 EXPLANATION

 A Win32 WriteFile() or winsock sendto() call failed. The return code is shown.

 ACTION

 Provide network support personnel with the event log file(s) related to SNA, and the return code included in this message. For information about SNA log files, see the “Microsoft Host Integration Server Online Books.

 The warning is caused because Host Integration Server tries to communicate with all the servers in a subdomain (and the clients) via broadcasts messages. Host Integration Server does this over all protocols that are selected in the SNA Manager. Not all of these protocols are enabled on your network, if one of them is not enabled this warning will pop up.

To fix this warning unselect the protocol that causes the warning as shown in this screenshot.

 

Typically you enable TCP/IP and you disable Named Pipes. After adapting the configuration the warning disappears.

 

Peter Borremans

WSDL import problem in BizTalk project

When adding a Web reference to a BizTalk project, the following message appears:

 

There is a problem with the WSDL but you don’t get any extra information.

The cause was this part in the WSDL:

 

    <port binding=”tns:TestServicePortBinding” name=”TestServicePort”>

            <soap:address location=”REPLACE_WITH_ACTUAL_URL“/>

    </port>

 

Apparently a BizTalk project can’t handle an invalid location (a Console project doesn’t have any problem with this!).

The problem was solved by putting the filepath of the WSDL in the location attribute.

To find this problem i removed part after part of the WSDL untill the import went fine. It’s a good practice to identify different kinds of WSDL problems.

 

WSCF.blue 1.0.7 supports VS10 RC

WSCF.blue 1.0.7 supports VS10 RC

WSCF.blue v1.0.7 now supports VS10 RC in addition to VS2008. There are no new features as such although v1.0.6 which we released a short while ago fixes a number of important bugs in data contract generation. Alex , our code-master extraordinaire, blogged about the 1.0.6 release here.
The generated code has not changed (from previous versions) […]

Application Platform Event – Netherlands

Application Platform Event – Netherlands

header

Haven’t blog for awhile since I am preparing for a session I giving on Monitoring Business Integration Suite (branded for SQL Server, BizTalk Server and MOSS in an integration context). This session will be given during the Dutch Application Platform Event. Will be my second talk I ever done for general public so one way I am quite excited (only time preparing a lot of stuff (demo’s) is hard task to do). Now I am enjoying my holiday currently in Chester (UK) and still cannot stay away from my laptop in the evenings 🙂

Update: Unfortunelty due to family matters and highly visible project within a customer I am working for I was unable to do the presentation. I regret it, but some things had higher priority.

Desktop Virtualization Hour – Coming March 18th

While browsing Microsoft’s Virtualization site, I stumbled across an upcoming event:

 

Looking at Desktop Virtualization including VDI? Thinking about Windows 7 migration; wants savings, but wondering about ROI?

Join Microsoft, industry experts and IT leaders: Desktop Virtualization Hour, March 18th, 9am PST.

This is all the information they’re sharing with the public at the moment, which normally might indicate a lack of planned content… but considering the fact that they purchased a domain just for this event, http://www.desktopvirtualizationhour.com/, Microsoft is being suspiciously vague about their plans.

I managed to find a ZDNet whitepaper posting from January that has some extra lines of description:

Have more questions than answers on the topic?

Watch and interact live with Microsoft, industry experts and IT leaders for a moderated televised discussion. Submit your questions in the hour or in advance.

Does the fact that these lines don’t appear on the event-specific domain mean something? Perhaps they’ve already chosen the questions they’re going to answer, or perhaps the list of experts grew too large to allow viewer participation? We’ll have to wait and see what transpires.

In the mean time, you can study up on VDI in preparation of this event:

  • Microsoft’s Virtualized Desktop Products and Technology – Homepage for Microsoft’s VDI solutions
  • TechNet video series on VDI deployment (Part 2, Part 3) – Matt McSpirit walks through setting up VDI

BizTalk Server: Handling decimal types with the xpath() Function

Last week, I spent some time looking at a problem with a customer’s orchestration. In one place, the developer had made use of the infamous BizTalk xpath() function to assign a value in one messageto a decimal field in another message. He got an error stating that the application is “Unable to cast object of type ‘System.Double’ to type ‘System.Xml.XmlNodeList'”. This is due to a logical error in BizTalk’s code. Read more at http://geekswithblogs.net/cyoung/archive/2010/02/23/biztalk-server-handling-decimal-types-with-the-xpath-function.aspx.