zoukankan      html  css  js  c++  java
  • 在WebApi的ActionFilter中读取ResponseContent内容遇到的问题

    最近用ActionFilter给REST Api加入本地缓存功能,在OnActionExecutedAsync重写中,需要将缓存对象的内容以byte[]的形式存入缓存,并缓存Etag、ContentType信息。

    而在该方法中以

    var content = await responseContent.ReadAsByteArrayAsync().ConfigureAwait(false);
    

    获取byte[]形式的响应内容时,提示Cannot access a closed Stream.即认为响应流已关闭,但其实上下文中并未显示关闭Stream对象或以using操作流对象。

    为了验证,同样的代码在一个新建的web api2项目中使用,则能够正常获取响应内容,查了很久未果。。。。

    以下是上下文代码:

     1 public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
     2         {
     3             if (actionExecutedContext.ActionContext.Response == null
     4                 || !actionExecutedContext.ActionContext.Response.IsSuccessStatusCode)
     5                 return;
     6             if (!IsCachingAllowed(actionExecutedContext.ActionContext))
     7                 return;
     8             var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
     9             if (cacheTime.AbsoluteExpiration > DateTime.Now)
    10             {
    11                 var httpConfig = actionExecutedContext.Request.GetConfiguration();
    12                 var config = httpConfig.RestCacheConfiguration();
    13                 var cacheKeyGenerator = config.GetCacheKeyGenerator(CacheKeyGenerator);
    14                 var responseMediaType = actionExecutedContext.Request.Properties[CurrentRequestMediaType] as MediaTypeHeaderValue
    15                     ?? GetExpectedMediaType(httpConfig, actionExecutedContext.ActionContext);
    16                 var cachekey = cacheKeyGenerator.MakeCacheKey(actionExecutedContext.ActionContext, responseMediaType);
    17                 if (!string.IsNullOrWhiteSpace(cachekey) && !(_restCache.Contains(cachekey)))
    18                 {
    19                     SetEtag(actionExecutedContext.Response, CreateEtag(actionExecutedContext, cachekey, cacheTime));
    20                     var responseContent = actionExecutedContext.Response.Content;
    21                     if (responseContent != null)
    22                     {
    23                         var baseKey = config.MakeBaseCachekey(actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName, actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
    24                         var contentType = responseContent.Headers.ContentType;
    25                         string etag = actionExecutedContext.Response.Headers.ETag.Tag;
    26                         try
    27                         {
    28                             var content = await responseContent.ReadAsByteArrayAsync().ConfigureAwait(false);
    29                             responseContent.Headers.Remove("Content-Length");
    30                             _restCache.Add(baseKey, string.Empty, cacheTime.AbsoluteExpiration);
    31                             _restCache.Add(cachekey, content, cacheTime.AbsoluteExpiration, baseKey);
    32                             _restCache.Add(cachekey + Constants.ContentTypeKey,
    33                                 contentType,
    34                                 cacheTime.AbsoluteExpiration, baseKey);
    35                             _restCache.Add(cachekey + Constants.EtagKey,
    36                                 etag,
    37                                 cacheTime.AbsoluteExpiration, baseKey);
    38                         }
    39                         catch (Exception exp)
    40                         {
    41                             //捕捉到Cannot access a closed Stream.
    42                             throw;
    43                         }
    44                     }
    45                 }
    46             }
    47             ApplyCacheHeaders(actionExecutedContext.ActionContext.Response, cacheTime);
    48         }
    View Code

    既然想以byte[]的形式缓存响应内容失效,只能另想办法,因此,直接缓存response content对象,在OnActionExecuting方法中取出该ObjectContent对象,再构建一个相同对象作为response content即可,以下为代码:

    OnActionExecuting:

    public override void OnActionExecuting(HttpActionContext actionContext)
            {
                if (actionContext == null) throw new ArgumentNullException("actionContext");
                if (!IsCachingAllowed(actionContext)) return;
                var config = actionContext.Request.GetConfiguration();
                EnsureCacheTimeQuery();
                EnsureCache(config);
                var cacheKeyGenerator = config.RestCacheConfiguration().GetCacheKeyGenerator(CacheKeyGenerator);
                var responseMediaType = GetExpectedMediaType(config, actionContext);
                actionContext.Request.Properties[CurrentRequestMediaType] = responseMediaType;
                var cachekey = cacheKeyGenerator.MakeCacheKey(actionContext, responseMediaType);
                if (!_restCache.Contains(cachekey))
                    return;
                if (actionContext.Request.Headers.IfNoneMatch != null)
                {
                    var etag = _restCache.Get<string>(cachekey + Constants.EtagKey);
                    if (etag != null)
                    {
                        if (actionContext.Request.Headers.IfNoneMatch.Any(x => x.Tag == etag))
                        {
                            var time = CacheTimeQuery.Execute(DateTime.Now);
                            var quickResponse = actionContext.Request.CreateResponse(HttpStatusCode.NotModified);
                            ApplyCacheHeaders(quickResponse, time);
                            actionContext.Response = quickResponse;
                            return;
                        }
                    }
                }
                var val = _restCache.Get<ObjectContent>(cachekey);
                if (val == null)
                    return;
                var contenttype = _restCache.Get<MediaTypeHeaderValue>(cachekey + Constants.ContentTypeKey) ?? responseMediaType;
                actionContext.Response = actionContext.Request.CreateResponse();
                var newContent = new ObjectContent<object>(val.Value, val.Formatter);
                actionContext.Response.Content = newContent;
                actionContext.Response.Content.Headers.ContentType = contenttype;
                var responseEtag = _restCache.Get<string>(cachekey + Constants.EtagKey);
                if (responseEtag != null)
                    SetEtag(actionContext.Response, responseEtag);
                var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
                ApplyCacheHeaders(actionContext.Response, cacheTime);
            }
    View Code

    OnActionExecutedAsync:

    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
            {
                if (actionExecutedContext.ActionContext.Response == null
                    || !actionExecutedContext.ActionContext.Response.IsSuccessStatusCode)
                    return;
                if (!IsCachingAllowed(actionExecutedContext.ActionContext))
                    return;
                var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
                if (cacheTime.AbsoluteExpiration > DateTime.Now)
                {
                    var httpConfig = actionExecutedContext.Request.GetConfiguration();
                    var config = httpConfig.RestCacheConfiguration();
                    var cacheKeyGenerator = config.GetCacheKeyGenerator(CacheKeyGenerator);
                    var responseMediaType = actionExecutedContext.Request.Properties[CurrentRequestMediaType] as MediaTypeHeaderValue
                        ?? GetExpectedMediaType(httpConfig, actionExecutedContext.ActionContext);
                    var cachekey = cacheKeyGenerator.MakeCacheKey(actionExecutedContext.ActionContext, responseMediaType);
                    if (!string.IsNullOrWhiteSpace(cachekey) && !(_restCache.Contains(cachekey)))
                    {
                        SetEtag(actionExecutedContext.Response, CreateEtag(actionExecutedContext, cachekey, cacheTime));
                        var responseContent = actionExecutedContext.Response.Content;
                        if (responseContent != null)
                        {
                            var baseKey = config.MakeBaseCachekey(actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName, actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
                            var contentType = responseContent.Headers.ContentType;
                            string etag = actionExecutedContext.Response.Headers.ETag.Tag;
                            try
                            {
                                responseContent.Headers.Remove("Content-Length");
                                _restCache.Add(baseKey, string.Empty, cacheTime.AbsoluteExpiration);
                                _restCache.Add(cachekey, responseContent, cacheTime.AbsoluteExpiration, baseKey);
                                _restCache.Add(cachekey + Constants.ContentTypeKey,
                                    contentType,
                                    cacheTime.AbsoluteExpiration, baseKey);
                                _restCache.Add(cachekey + Constants.EtagKey,
                                    etag,
                                    cacheTime.AbsoluteExpiration, baseKey);
                            }
                            catch (Exception exp)
                            {
                                //捕捉到Cannot access a closed Stream.
                                throw;
                            }
                        }
                    }
                }
                ApplyCacheHeaders(actionExecutedContext.ActionContext.Response, cacheTime);
            }
    View Code

    相同的代码在不同上下文中对流操作表现不同,该问题还未找到原因,有待排查。

  • 相关阅读:
    DS博客作业04--图
    DS博客作业03--树
    DS博客作业02--栈和队列
    DS博客作业01--线性表
    C博客作业05--指针
    C语言博客作业04--数组
    C博客作业03--函数
    博客作业——循环结构
    C博客作业05-指针
    C博客作业04--数组
  • 原文地址:https://www.cnblogs.com/you-you-111/p/7987208.html
Copyright © 2011-2022 走看看