如何实现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
相关产品推荐
相关产品推荐

