获取if-modified-since标头以使用WCF

时间:2011-10-27 17:12:40

标签: wcf http-headers etag if-modified-since

我正在尝试使用“if-modified-since”标头来使用我的WCF Web服务。

当用户向我的服务发出请求时,我会将ETag添加到包含请求时间戳的传出响应中,如下所示:

var tag = String.Format("\"{0:o}\"", new DateTimeOffset(DateTime.Now));

这导致以下ETag标题:

ETag: "2011-10-27T13:09:39.6242263-04:00"

然后我接受这个值并将其作为if-modified-since标头回显给后续请求,如下所示:

If-Modified-Since:2011-10-27T13:09:39.6242263-04:00

当我检查WebOperationContext.Current.Headers.IfModifiedSince时,我从未获得提供的值。该值固定为“12/31/1969 7:00:00 PM”。

我做错了什么?

更新

我应该添加使用Fiddler,我可以将任何值设置为If-Modified-Since标头,并且仍然在代码中获得相同的1969值。

1 个答案:

答案 0 :(得分:2)

首先,If-Modified-Since是关于资源最后修改时间的条件GET,而ETag是关于资源标识符的条件GET,所以请小心混合两个概念。

在WCF服务中实现对If-Modified-Since的支持的正确方法是使用CheckConditionalRetrieveDateTime对象中传递WebOperationContext.Current.IncomingRequest值 - 请参阅下面的代码那。如果IMS标头的值在您传递给CheckConditionalRetrieve的日期之前,则该方法将在该点退出,返回304(未修改)响应。否则它会继续。下面的代码显示了。

另一个问题:即使通过您正在使用的日期格式(ISO 8601)也可以使用,但根据规范(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html中的第14.25节和http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1中的第3.3.1节)它是不正确的),所以你应该考虑使用有效的格式来防止将来出现问题。

您可以在http://blogs.msdn.com/b/endpoint/archive/2010/02/25/conditional-get-and-etag-support-in-wcf-webhttp-services.aspx的WCF中找到关于条件GET支持的好文章。

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;
    }
}