First off, If-Modified-Since
is about conditional GETs regarding the time of the last modification of the resource, while ETag
is about conditional GETs regarding an identifier of the resources, so please be careful with mixing the two concepts.
The correct way of implementing support for If-Modified-Since in a WCF service is to use the CheckConditionalRetrieve
passing a DateTime
value in the WebOperationContext.Current.IncomingRequest
object - see the code below for that. If the value of the IMS header is before the date you pass to CheckConditionalRetrieve
, the method will exit at that point returning a 304 (Not Modified) response. Otherwise it will just continue. The code below shows that.
Another issue: even through the date format you're using (ISO 8601) works, but it's not correct based on the specification (section 14.25 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, and section 3.3.1 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1), so you should consider using a valid format to prevent future problems.
You can find a good post about conditional GET support in WCF at http://blogs.msdn.com/b/endpoint/archive/2010/02/25/conditional-get-and-etag-support-in-wcf-webhttp-services.aspx.
public class StackOverflow_7919718
{
[ServiceContract]
public class Service
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public string GetData()
{
Console.WriteLine("If-Modified-Since header (1): {0}", WebOperationContext.Current.IncomingRequest.IfModifiedSince);
WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(DateTime.UtcNow);
return "Data";
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
Console.WriteLine("Not sending If-Modified-Since header (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null);
Console.WriteLine("Sending data in the past, ISO 8601 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2011-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the future, ISO 8601 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2021-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the past, RFC 1123 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Wed, 26 Oct 2011 01:00:00 GMT" } });
Console.WriteLine("Sending data in the future, RFC 1123 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Mon, 27 Oct 2031 10:00:00 GMT" } });
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
public static class Util
{
public static string SendRequest(string uri, string method, string contentType, string body)
{
return SendRequest(uri, method, contentType, body, null);
}
public static string SendRequest(string uri, string method, string contentType, string body, Dictionary<string, string> headers)
{
string responseBody = null;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (headers != null)
{
foreach (string headerName in headers.Keys)
{
switch (headerName)
{
case "If-Modified-Since":
req.IfModifiedSince = DateTime.Parse(headers[headerName]);
break;
default:
req.Headers[headerName] = headers[headerName];
break;
}
}
}
if (!String.IsNullOrEmpty(contentType))
{
req.ContentType = contentType;
}
if (body != null)
{
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
req.GetRequestStream().Close();
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
if (resp == null)
{
responseBody = null;
Console.WriteLine("Response is null");
}
else
{
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Stream respStream = resp.GetResponseStream();
if (respStream != null)
{
responseBody = new StreamReader(respStream).ReadToEnd();
Console.WriteLine(responseBody);
}
else
{
Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
}
}
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
return responseBody;
}
}