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

.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);
        }
    }
}

问题原因
  1. IHttpActionResult执行时机问题:OnActionExecuted触发时,IHttpActionResult尚未真正执行生成完整响应,此时缓存的HttpContent可能处于未初始化状态。
  2. HttpContent的单次读取限制:HttpContent对象只能被读取一次,直接缓存该对象后再次读取会引发内部错误(导致500)。
  3. 媒体类型不匹配:返回缓存内容时未匹配请求的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);
    }
}

关键优化点

  1. 改用异步Task方法:替换async void为async Task,避免异步操作中异常无法被捕获的问题,符合Web API异步规范。
  2. 缓存序列化后的内容:将HttpContent读取为字符串后缓存,规避HttpContent单次读取的限制。
  3. 保留媒体类型信息:缓存时记录响应的Content-Type,返回时匹配请求的Accept头,解决406错误。
  4. 仅缓存成功响应:避免缓存错误状态的响应内容。

内容的提问来源于stack exchange,提问作者Davood Arjmandi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:54:52