如何通过操作添加控制缓存服务的缓存?

时间:2014-06-26 07:38:20

标签: wcf rest caching

我已经使用WCF编写了休息服务。该服务包含多个操作。有些是基于GET的([WebGet]),有些是基于POST的([WebInvoke])。

该服务正在按预期工作。但是,基于GET的操作放在客户端缓存中,这对于所有操作都是不可取的。

经过一番搜索后,我找到了How to prevent the browser from caching WCF JSON responses。这是有效的,但我发现它不是非常可重复使用。

我的平台不允许我更新web.config。实际上,我的服务是SharePoint项目的一部分。并且更新web.config文件很难正确实现。这禁止我使用[WebCache]属性。

所以我实现了一个自定义MessageInspector来修复正确的标题:

public class CacheAttribute : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
    {
        foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
        {
            foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
            {
                eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
            }
        }
    }
     /*...
        Other code omitted for brievty
     */

}

public class CacheInspector : IDispatchMessageInspector
{
     /*...
        Code omitted for brievety
     */

    public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        var cache = HttpContext.Current.Response.Cache;

        if (m_CacheEnabled)
        {
            cache.SetCacheability(HttpCacheability.Public);
            cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);

        }
        else
        {
            cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
            cache.SetNoStore();
        }

    }
}

此代码按预期工作,但它适用于服务中的所有操作。

如何编写应用相同逻辑的基于属性的类,但是在操作范围

我试图在IOperationBehavior界面中找到有用的东西,但我找不到合适的实施方式。

完整代码(.net 4.5):

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class CacheAttribute : Attribute, IServiceBehavior
{

    private readonly bool m_CacheEnabled;

    public bool CacheEnabled  { get { return m_CacheEnabled; } }

    public TimeSpan? CacheDuration { get; set; } 

    public CacheAttribute(bool cacheEnabled)
    {
        this.m_CacheEnabled = cacheEnabled;
    }
    public CacheAttribute(TimeSpan cacheDuration) : this(true)
    {
        this.CacheDuration = cacheDuration;
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
    {
        foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
        {
            foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
            {
                eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
            }
        }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }



    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

}

public class CacheInspector : IDispatchMessageInspector
{
    private readonly bool m_CacheEnabled;
    private readonly TimeSpan? CacheDuration;

    public CacheInspector(bool m_CacheEnabled, TimeSpan? CacheDuration)
    {
        this.m_CacheEnabled = m_CacheEnabled;
        this.CacheDuration = CacheDuration;
    }
    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
    {
        return null;
    }

    public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        var cache = HttpContext.Current.Response.Cache;

        if (m_CacheEnabled)
        {
            cache.SetCacheability(HttpCacheability.Public);
            cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);

        }
        else
        {
            cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
            cache.SetNoStore();
        }

    }
}

1 个答案:

答案 0 :(得分:5)

我认为这正是您所寻找的。

[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute, IOperationBehavior, IParameterInspector
{
    public TimeSpan CacheLifetime { get; private set; }

    public CacheAttribute(double lifetime)
    {
        this.CacheLifetime = TimeSpan.FromSeconds(lifetime);
    }

    #region IOperationBehavior Members

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {}

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) {}

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.ParameterInspectors.Add(this);
    }

    public void Validate(OperationDescription operationDescription) {}

    #endregion

    #region IParameterInspector Members

    public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
    {
        if (this.CacheLifetime == TimeSpan.Zero) {
            WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
        } else {
            WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", string.Format("max-age={0}",this.CacheLifetime.TotalSeconds));
        }
    }

    public object BeforeCall(string operationName, object[] inputs)
    {
        return null;
    }

    #endregion
}

使用

[ServiceContract]
public interface ICacheTestService
{
    [OperationContract]
    [WebGet(UriTemplate = "CurrentTime")]
    [Cache(0)]
    string GetCurrentTime();

    [OperationContract]
    [WebGet(UriTemplate = "CurrentTimeCached")]
    [Cache(30)]
    string GetCurrentTimeCached();

    [OperationContract]
    [WebGet(UriTemplate = "CurrentTimeNoCC")]
    string GetCurrentTimeNoCC();
}