You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现WCF缓存行为以直接返回自定义响应?

WCF服务缓存行为实现方案

核心实现思路

通过自定义服务特性+消息拦截器的组合,实现请求级别的JSON响应缓存:用请求URL作为缓存键,请求进入时检查缓存,命中则直接返回缓存的JSON;未命中则执行服务方法,响应返回时将JSON写入缓存。


1. 定义CacheBehavior服务特性

这个特性用于标记需要启用缓存的服务类,同时负责将自定义消息拦截器注入WCF管道:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

[AttributeUsage(AttributeTargets.Class)]
public class CacheBehaviorAttribute : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
        // 无需额外处理
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        // 为所有端点绑定缓存消息拦截器
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
            {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheMessageInspector());
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        // 无需验证逻辑
    }
}

2. 实现缓存消息拦截器CacheMessageInspector

实现IDispatchMessageInspector接口,处理请求的缓存检查和响应的缓存写入:

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;

public class CacheMessageInspector : IDispatchMessageInspector
{
    // 示例使用MemoryCache,生产环境可替换为Redis等分布式缓存
    private static readonly System.Runtime.Caching.MemoryCache _cache = System.Runtime.Caching.MemoryCache.Default;
    private const string CacheKeyPrefix = "WcfServiceCache_";

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        // 提取请求URL作为缓存键
        var requestUri = request.Headers.To.ToString();
        var cacheKey = CacheKeyPrefix + requestUri;

        // 检查缓存命中
        if (_cache.Contains(cacheKey))
        {
            var cachedJson = _cache.Get(cacheKey) as string;
            if (!string.IsNullOrEmpty(cachedJson))
            {
                // 构造缓存响应消息,跳过服务方法执行
                var responseMessage = Message.CreateMessage(request.Version, null, new RawJsonBodyWriter(cachedJson));
                responseMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json));
                
                var httpResponseProp = new HttpResponseMessageProperty();
                httpResponseProp.Headers.Add(System.Net.HttpRequestHeader.ContentType, "application/json");
                responseMessage.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProp);

                // 抛出异常中断管道,直接返回缓存响应
                throw new FaultException(new FaultReason("Cached response"), new FaultCode("Cache"), responseMessage);
            }
        }

        // 返回缓存键,供响应阶段使用
        return cacheKey;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        var cacheKey = correlationState as string;
        if (string.IsNullOrEmpty(cacheKey) || _cache.Contains(cacheKey)) return;

        // 读取响应的JSON内容
        var buffer = reply.CreateBufferedCopy(int.MaxValue);
        var copyMessage = buffer.CreateMessage();
        reply = buffer.CreateMessage(); // 恢复原始消息

        var webBodyFormat = reply.Properties[WebBodyFormatMessageProperty.Name] as WebBodyFormatMessageProperty;
        if (webBodyFormat?.Format == WebContentFormat.Json)
        {
            using (var reader = copyMessage.GetReaderAtBodyContents())
            {
                reader.MoveToContent();
                var jsonContent = reader.ReadInnerXml();
                // 写入缓存,设置5分钟过期时间(可按需调整)
                _cache.Add(cacheKey, jsonContent, DateTimeOffset.Now.AddMinutes(5));
            }
        }
    }

    // 自定义BodyWriter,用于直接输出JSON字符串
    private class RawJsonBodyWriter : BodyWriter
    {
        private readonly string _jsonContent;
        public RawJsonBodyWriter(string json) : base(true)
        {
            _jsonContent = json;
        }

        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            writer.WriteRaw(_jsonContent);
        }
    }
}

3. 启用缓存特性

直接在服务类上标记[CacheBehavior]即可:

[CacheBehavior]
public class MyService : IMyService
{
    public string GetUserInfo(int userId)
    {
        // 实际业务逻辑,未命中缓存时才会执行
        return $"User {userId} Info";
    }
}

关于之前尝试组件的说明

  • ChannelMessageInterceptor/ChannelDispatcherBase:偏底层传输管道处理,不适合业务级缓存逻辑的封装
  • IOperationInvoker:仅能拦截方法调用,难以直接获取请求URL和处理响应的JSON格式转换
  • IDispatchMessageInspector:本身是合适的选择,之前未成功可能是未正确处理缓存响应的管道中断逻辑,或未完成响应内容的读取与缓存写入

注意事项

  • 若需支持POST请求,需将请求体内容加入缓存键(避免不同请求体用同一URL键缓存)
  • 生产环境建议替换MemoryCache为分布式缓存,避免单点缓存失效
  • 可扩展CacheBehavior特性,添加缓存过期时间、忽略操作列表等配置参数

内容的提问来源于stack exchange,提问作者Trevor Elliott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:12:41