.NET Framework 4.8中OnActionExecuting返回缓存报406/500错误求助
问题描述
在OnActionExecuted(HttpActionExecutedContext actionExecutedContext)中将响应内容存入CacheManagerService缓存,但在OnActionExecuting(HttpActionContext actionContext)中读取缓存返回时,会出现HTTP 406或500错误且无详细描述。该逻辑在控制器返回Task<someClass>时正常工作,但在返回Task<IHttpActionResult>的ODataController中失效。
用户提供的代码:
using System.Net.Http; using System.Net; using Newtonsoft.Json; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Http; namespace ***.Mvc.ActionFilters { public class SampleAttribute : ActionFilterAttribute { public override async void OnActionExecuting(HttpActionContext actionContext) { base.OnActionExecuting(actionContext); var respCache = GetCache(key); if (respCache != null) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, await resp.ReadAsAsync(typeof(object))); } } public override async void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); SetCache(key,actionExecutedContext.Response.Content); } } }
问题原因
IHttpActionResult执行时机问题:OnActionExecuted触发时,IHttpActionResult尚未真正执行生成完整响应,此时缓存的HttpContent可能处于未初始化状态。HttpContent的单次读取限制:HttpContent对象只能被读取一次,直接缓存该对象后再次读取会引发内部错误(导致500)。- 媒体类型不匹配:返回缓存内容时未匹配请求的
Accept头,导致406(无法接受的媒体类型)错误。
解决方法
修正后的代码
using System.Net.Http; using System.Net; using System.Net.Http.Headers; using Newtonsoft.Json; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Http; using System.Threading.Tasks; namespace ***.Mvc.ActionFilters { public class SampleAttribute : ActionFilterAttribute { // 缓存项模型,存储内容和对应媒体类型 private class CacheItem { public string Content { get; set; } public string MediaType { get; set; } } public override async Task OnActionExecutingAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken) { await base.OnActionExecutingAsync(actionContext, cancellationToken); var key = GetCacheKey(actionContext); var cacheItem = GetCache(key) as CacheItem; if (cacheItem != null) { // 构建匹配请求媒体类型的响应内容 var responseContent = new StringContent(cacheItem.Content); responseContent.Headers.ContentType = MediaTypeHeaderValue.Parse(cacheItem.MediaType); actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK); actionContext.Response.Content = responseContent; } } public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, System.Threading.CancellationToken cancellationToken) { await base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); // 仅缓存成功状态的响应 if (actionExecutedContext.Response?.IsSuccessStatusCode != true) return; var key = GetCacheKey(actionExecutedContext.ActionContext); var content = actionExecutedContext.Response.Content; // 读取内容为字符串,避免HttpContent只能单次读取的问题 var contentString = await content.ReadAsStringAsync(); var mediaType = content.Headers.ContentType?.MediaType ?? "application/json"; var cacheItem = new CacheItem { Content = contentString, MediaType = mediaType }; SetCache(key, cacheItem); } // 自定义缓存键生成逻辑,可根据请求路径、参数、Accept头等调整 private string GetCacheKey(HttpActionContext actionContext) { var request = actionContext.Request; var acceptHeader = request.Headers.Accept.FirstOrDefault()?.MediaType ?? "application/json"; return $"{request.RequestUri.PathAndQuery}_{acceptHeader}"; } // 替换为实际的CacheManagerService调用 private object GetCache(string key) => CacheManagerService.Get(key); private void SetCache(string key, object value) => CacheManagerService.Set(key, value); } }
关键优化点
- 改用异步Task方法:替换
async void为async Task,避免异步操作中异常无法被捕获的问题,符合Web API异步规范。 - 缓存序列化后的内容:将
HttpContent读取为字符串后缓存,规避HttpContent单次读取的限制。 - 保留媒体类型信息:缓存时记录响应的
Content-Type,返回时匹配请求的Accept头,解决406错误。 - 仅缓存成功响应:避免缓存错误状态的响应内容。
内容的提问来源于stack exchange,提问作者Davood Arjmandi
相关产品推荐
相关产品推荐

