在WebApi的ActionFilter中讀取ResponseContent內容遇到的問題

最近用ActionFilter給REST Api加入本地緩存功能,在OnActionExecutedAsync重寫中,須要將緩存對象的內容以byte[]的形式存入緩存,並緩存Etag、ContentType信息。web

而在該方法中以api

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

獲取byte[]形式的響應內容時,提示Cannot access a closed Stream.即認爲響應流已關閉,但其實上下文中並未顯示關閉Stream對象或以using操做流對象。緩存

 

爲了驗證,一樣的代碼在一個新建的web api2項目中使用,則可以正常獲取響應內容,查了好久未果。。。。async

如下是上下文代碼:ide

 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便可,如下爲代碼:ui

OnActionExecuting:spa

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:3d

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

 


 

相同的代碼在不一樣上下文中對流操做表現不一樣,該問題還未找到緣由,有待排查。rest

相關文章
相關標籤/搜索