Setting HTTP Headers in WCF (.NET 3.5)

Published 22 August 07 10:41 AM | justinjsmith 

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: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html

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 3 4 // set the Content-Type 5 WebOperationContext.Current.OutgoingResponse.ContentType = "text/html"; 6 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) { 2 3 // normal implementation would go here 4 5 // set the Content-Type 6 WebOperationContext.Current.OutgoingResponse.ContentType = "text/html"; 7 8 // call a helper method that sets cache-control header 9 SetCaching(WebOperationContext.Current, DateTime.Now, 120); 10 11 // return something 12 return someNumber.ToString(); 13 } 14 15 private void SetCaching(WebOperationContext context, DateTime lastModifiedDate, Int32 maxCacheAge){ 16 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); 21 22 // set cache validation 23 context.OutgoingResponse.LastModified = lastModifiedDate; 24 String eTag = context.IncomingRequest.UriTemplateMatch.RequestUri.ToString() + lastModifiedDate.ToString(); 25 context.OutgoingResponse.ETag = eTag; 26 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.

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Kevin Daly said on August 22, 2007 4:28 PM:

One little thing: in Beta 2 there's a bug in the way LastModifiedDate is handled that causes the LastModified header to be set to an invalid value for HTTP dates (at least in the scenario in which I tried it - see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=289942&wa=wsignin1.0). You can get around this by setting the header directly to (per your example) lastModifiedDate.ToUniversalTime().ToString("r") (for example).

Hopefully this will be fixed for RTM: overall this is very cool - the bit I've mentioned is important for conditional get, which is why I'm keen to get it sorted out.

# Christopher Steen said on August 25, 2007 3:43 PM:

Link Listing - August 25, 2007

# Darrel Miller said on September 7, 2007 7:59 PM:

Any insight on how System.ServiceModel.Web works would be greatly appreciated.

After using it since the release of the BizTalk Services Labs, I only discovered today that I can actually return a stream directly!  I had previously been returning a Message that was created from a custom class derived from BodyWriter.  

I found some details about doing SSL from a blog entry from Gudge but I have still not been able to retrieve client credentials on the server side.

Like I said, any pointers would be appreciated.

# Awnik said on February 6, 2008 10:54 AM:

HI ,

I added a reference to System.ServiceModel.Web. and also Imported the System.ServiceModel.Web namespace .

Imports System.ServiceModel.Web

But still following line of code shows error

System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.ContentType = "text/html"

Error 3 'Web' is not a member of 'ServiceModel'. F:\Job_Core\testground\WCFTestProject\WCFTestProject\WCFTestProject\Service1.vb 11 WCFTestProject

Can you please help

# All About Interop said on April 3, 2008 11:39 AM:

My buddy Justin wrote about how to set the Content-Type headers in a WebGet method in a WCF REST app.

Leave a Comment

(required) 
(optional)
(required) 
Page view tracker