您可以使用自定义消息检查器来添加Vary
响应的标题。基于WCF WebHTTP 的自动格式化规则 http://msdn.microsoft.com/en-us/library/ee476510.aspx,顺序为1)Accept header; 2)请求消息的Content-Type; 3)操作中的默认设置和4)行为本身的默认设置。只有前两个依赖于请求(从而影响Vary
header),对于您的场景(缓存),只有 GET 感兴趣,因此我们也可以丢弃传入的 Content-Type。所以编写这样一个检查器相当简单:如果AutomaticFormatSelectionEnabled
设置了属性,然后我们添加Vary: Accept
所有 GET 请求的响应的标头 - 下面的代码就是这样做的。如果您想包含内容类型(也适用于非 GET 请求),您可以修改检查器以查看传入的请求。
public class Post_0acbfef2_16a3_440a_88d6_e0d7fcf90a8e
{
[DataContract(Name = "Person", Namespace = "")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
[ServiceContract]
public class MyContentNegoService
{
[WebGet(ResponseFormat = WebMessageFormat.Xml)]
public Person ResponseFormatXml()
{
return new Person { Name = "John Doe", Age = 33 };
}
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public Person ResponseFormatJson()
{
return new Person { Name = "John Doe", Age = 33 };
}
[WebGet]
public Person ContentNegotiated()
{
return new Person { Name = "John Doe", Age = 33 };
}
[WebInvoke]
public Person ContentNegotiatedPost(Person person)
{
return person;
}
}
class MyVaryAddingInspector : IEndpointBehavior, IDispatchMessageInspector
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
WebHttpBehavior webBehavior = endpoint.Behaviors.Find<WebHttpBehavior>();
if (webBehavior != null && webBehavior.AutomaticFormatSelectionEnabled)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty prop;
prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
if (prop.Method == "GET")
{
// we shouldn't cache non-GET requests, so only returning this for such requests
return "Accept";
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
string varyHeader = correlationState as string;
if (varyHeader != null)
{
HttpResponseMessageProperty prop;
prop = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
if (prop != null)
{
prop.Headers[HttpResponseHeader.Vary] = varyHeader;
}
}
}
}
public static void SendGetRequest(string uri, string acceptHeader)
{
SendRequest(uri, "GET", null, null, acceptHeader);
}
public static void SendRequest(string uri, string method, string contentType, string body, string acceptHeader)
{
Console.Write("{0} request to {1}", method, uri.Substring(uri.LastIndexOf('/')));
if (contentType != null)
{
Console.Write(" with Content-Type:{0}", contentType);
}
if (acceptHeader == null)
{
Console.WriteLine(" (no Accept header)");
}
else
{
Console.WriteLine(" (with Accept: {0})", acceptHeader);
}
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (contentType != null)
{
req.ContentType = contentType;
Stream reqStream = req.GetRequestStream();
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
reqStream.Write(bodyBytes, 0, bodyBytes.Length);
reqStream.Close();
}
if (acceptHeader != null)
{
req.Accept = acceptHeader;
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
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();
Console.WriteLine(new StreamReader(respStream).ReadToEnd());
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(MyContentNegoService), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(MyContentNegoService), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior { AutomaticFormatSelectionEnabled = true });
endpoint.Behaviors.Add(new MyVaryAddingInspector());
host.Open();
Console.WriteLine("Host opened");
foreach (string operation in new string[] { "ResponseFormatJson", "ResponseFormatXml", "ContentNegotiated" })
{
foreach (string acceptHeader in new string[] { null, "application/json", "text/xml", "text/json" })
{
SendGetRequest(baseAddress + "/" + operation, acceptHeader);
}
}
Console.WriteLine("Sending some POST requests with content-nego (but no Vary in response)");
string jsonBody = "{\"Name\":\"John Doe\",\"Age\":33}";
SendRequest(baseAddress + "/ContentNegotiatedPost", "POST", "text/json", jsonBody, "text/xml");
SendRequest(baseAddress + "/ContentNegotiatedPost", "POST", "text/json", jsonBody, "text/json");
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}