Setting HTTP Headers in WCF (.NET 3.5)

One of the cool parts of WCF in the .NET 3.5 is the simplified support of the REST architectural style. URIs, HTTP verbs, and HTTP headers are 1st class citizens in the programming model. Each of these citizens is interesting. I'll focus a bit on HTTP headers here.

HTTP headers dictate a wide array of characteristics of how a server responds to a request and how a client reacts to the response sent by the server. A full discussion is beyond the scope of my blog post, but this stuff has been around for quite a while. You should be able to do a Live Search on HTTP Headers to dig up more info on their uses.

There's two uses that are particularly interesting from a services perspective: Content-Type and Cache-Control.

Content-Type serves as a way for the server to indicate the data format and is expressed as a MIME type label. SOAP / WS-* services generally express data format in terms of schema and WSDL. Schema and WSDL are highly expressive, whereas Content-Type is dramatically simpler. The web tends to rely on Content-Type.

The Cache-Control response header indicates how long a response is valid. It also can include instructions regarding how the client should validate their cache. You can get the particulars straight from the horses mouth:

The easiest way to set the value of these headers in a WCF application is via the WebOperationContext type. An object of this type is normally available in a method of a service object, and follows the usage pattern demonstrated in .NET 3.0's OperationContext type.

Here's how you can set he Content-Type header in a method of a service object (for the HTTP wonks, I've omitted charset):

 1  public String SomeMethod(Int32 someNumber) {
2   // normal implementation would go here
4   // set the Content-Type
5   WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
7   // return something
8   return someNumber.ToString();
9 }

Here's how you could set the Cache-Control response header and the validation HTTP headers:

  1 public String SomeMethod(Int32 someNumber) {
 3     // normal implementation would go here
 5     // set the Content-Type
 6     WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
 8     // call a helper method that sets cache-control header
 9     SetCaching(WebOperationContext.Current, DateTime.Now, 120);
11     // return something
12     return someNumber.ToString();
13 }
15 private void SetCaching(WebOperationContext context, DateTime lastModifiedDate, Int32 maxCacheAge){
17     // set CacheControl header
18     HttpResponseHeader cacheHeader = HttpResponseHeader.CacheControl;
19     String cacheControlValue = String.Format("max-age={0}, must-revalidate", maxCacheAge);
20     context.OutgoingResponse.Headers.Add(cacheHeader, cacheControlValue);
22     // set cache validation 
23     context.OutgoingResponse.LastModified = lastModifiedDate;
24     String eTag = context.IncomingRequest.UriTemplateMatch.RequestUri.ToString() + lastModifiedDate.ToString();
25     context.OutgoingResponse.ETag = eTag;
27 }

There are lots of different options available for caching and validation. If there's interest, I will dive into those in a future post. It's hard to gauge what's common knowledge about these headers.

From a debugging perspective, know that Fiddler is your best friend. When you are watching headers on localhost services, you won't see the sessions in Fiddler unless you use the full machine name in the request. The machine name forces the request to go through the fiddler proxy.