public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            await base.OnActionExecutionAsync(context, next);

            if (
                context.HttpContext.Response != null &&
                !(
                    context.HttpContext.Response.StatusCode >= (int)HttpStatusCode.OK &&
                    context.HttpContext.Response.StatusCode < (int)HttpStatusCode.Ambiguous
                    )
                )
            {
                return;
            }

            IServiceProvider   serviceProvider   = context.HttpContext.RequestServices;
            IApiOutputCache    cache             = serviceProvider.GetService(typeof(IApiOutputCache)) as IApiOutputCache;
            ICacheKeyGenerator cacheKeyGenerator = serviceProvider.GetService(typeof(ICacheKeyGenerator)) as ICacheKeyGenerator;

            if (cache != null && cacheKeyGenerator != null)
            {
                string controllerName = this.controller ??
                                        (context.ActionDescriptor as ControllerActionDescriptor)?.ControllerTypeInfo.FullName;

                string baseCachekey = cacheKeyGenerator.MakeBaseCachekey(controllerName, this.methodName);

                await cache.RemoveStartsWithAsync(baseCachekey);
            }
        }
示例#2
0
 protected override void EnsureCache(HttpConfiguration config, HttpRequestMessage req)
 {
     _webApiCache = config.CacheOutputConfiguration().GetCacheOutputProvider(req);
     if (_webApiCache is CustomCacheProvider)
     {
         ((CustomCacheProvider)_webApiCache).ExpirationMode = this.ExpirationMode;
     }
 }
        protected virtual void EnsureCache(HttpConfiguration config, HttpRequestMessage req)
        {
            object cache;
            config.Properties.TryGetValue(typeof (IApiOutputCache), out cache);

            var cacheFunc = cache as Func<IApiOutputCache>;

            _webApiCache = cacheFunc != null ? cacheFunc() : req.GetDependencyScope().GetService(typeof(IApiOutputCache)) as IApiOutputCache ?? new MemoryCacheDefault();
        }
        /// <summary>
        /// Classe para pegar o cache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <param name="resultGetter"></param>
        /// <param name="bypassCache"></param>
        /// <returns></returns>
        public static T GetCachedResult <T>(this IApiOutputCache cache, string key, DateTimeOffset expiry, Func <T> resultGetter, bool bypassCache = true) where T : class
        {
            var result = cache.Get <T>(key);

            if (result == null || bypassCache)
            {
                result = resultGetter();
                if (result != null)
                {
                    cache.Add(key, result, expiry);
                }
            }

            return(result);
        }
 protected virtual void EnsureCache(HttpConfiguration config, HttpRequestMessage req)
 {
     _webApiCache = config.CacheOutputConfiguration().GetCacheOutputProvider(req);
 }
 protected virtual void EnsureCache(HttpConfiguration config, HttpRequestMessage req)
 {
     _webApiCache = config.CacheOutputConfiguration().GetCacheOutputProvider(req);
 }
示例#7
0
 public CacheOutputCacheProvider(IApiOutputCache cache)
 {
     this.cacheConfig = GlobalConfiguration.Configuration.CacheOutputConfiguration();
     this.cache       = cache;
 }
示例#8
0
        /// <summary>
        /// Generates a cache key containing the namespace/controller/action, and name/value pairs for action arguments.
        /// Query string parameters that do not map to an action parameter are not included.
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="actionContext"></param>
        /// <param name="mediaType"></param>
        /// <param name="controllerLowered"></param>
        /// <param name="actionLowered"></param>
        /// <returns></returns>
        public async Task <string> MakeCacheKeyAsync(IApiOutputCache cache, HttpActionContext actionContext, MediaTypeHeaderValue mediaType, string controllerLowered, string actionLowered)
        {
            // The default set of action argument names/values that make up the cache key:
            //   * name=value of all default URI-bindlable action parameters.
            //   * name=val1;val2;val3 of all URI-bindable IEnumerable action parameters.
            //   * prop1name=prop1val, etc. of all public instance properties of URI-bindable, non-IEnumerable action parameters (aka, view models or DTOs).
            //
            // The key point is that they must match up to a named parameter so that the invalidation logic can
            //   access the value and increment its counter.

            var allActionParameters = actionContext.ActionDescriptor.GetParameters();


            //
            // Get name=value pairs from "simple" Web API action parameters (i.e., URI-bound by default).
            //

            var defaultUriBindableArgNamesValues = new List <KeyValuePair <string, string> >();
            var defaultUriBindableActionParams   = allActionParameters.Where(ap => ap.ParameterType.IsDefaultUriBindableType()).ToList();

            foreach (var defaultUriBindableActionParam in defaultUriBindableActionParams)
            {
                var actionArg = actionContext.ActionArguments.Single(kvp => kvp.Key == defaultUriBindableActionParam.ParameterName);
                defaultUriBindableArgNamesValues.Add(new KeyValuePair <string, string>(actionArg.Key, actionArg.Value.GetValueAsString()));
            }


            //
            // Get name=value pairs from complex types that are explicitly URI-bound via FromUriAttribute.
            //

            var nonDefaultUriBindableArgNamesValues = new List <KeyValuePair <string, string> >();
            var nonDefaultUriBindableActionParams   = allActionParameters.Where(ap => !ap.ParameterType.IsDefaultUriBindableType() && ap.IsUriBindableParameter()).ToList();

            foreach (var nonDefaultUriBindableActionParam in nonDefaultUriBindableActionParams)
            {
                // Get the corresponding action argument matching the parameter name.
                var actionArg = actionContext.ActionArguments.Single(kvp => kvp.Key == nonDefaultUriBindableActionParam.ParameterName);

                if (typeof(IEnumerable).IsAssignableFrom(nonDefaultUriBindableActionParam.ParameterType))
                {
                    // It's an array or list of some type. Join its values as a semicolon-separated string.
                    nonDefaultUriBindableArgNamesValues.Add(new KeyValuePair <string, string>(actionArg.Key, actionArg.Value.GetValueAsString()));
                }
                else
                {
                    // It's a view model/dto. We need its public instance property names and values.
                    if (actionArg.Value != null)
                    {
                        // Get the names/values of its public instance properties.
                        var pubInstProps = actionArg.Value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
                        foreach (var pubInstProp in pubInstProps)
                        {
                            nonDefaultUriBindableArgNamesValues.Add(new KeyValuePair <string, string>(pubInstProp.Name, pubInstProp.GetValue(actionArg.Value).GetValueAsString()));
                        }
                    }
                    else
                    {
                        // The object is null. We still need its public instance property names. If a query string parameter of the
                        //   same name exists, we'll use its value. Otherwise, we'll use the value from a new instance of the parameter
                        //   type.
                        var objInstance  = Activator.CreateInstance(nonDefaultUriBindableActionParam.ParameterType);
                        var pubInstProps = nonDefaultUriBindableActionParam.ParameterType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

                        // Exclude jsonp callback parameters, if any.
                        var qsParams = actionContext.Request.GetQueryNameValuePairs()
                                       .Where(x => x.Key.ToLower() != "callback")
                                       .ToArray();

                        foreach (var pubInstProp in pubInstProps)
                        {
                            // Case insenstitive compare because query string parameter names can come in as any case.
                            var matchingQsParams = qsParams.Where(kvp => kvp.Key.Equals(pubInstProp.Name, StringComparison.OrdinalIgnoreCase)).ToArray();

                            if (matchingQsParams.Length > 0)
                            {
                                // When the target is a non-collection, scalar value, the default model binder only selects the first value if
                                //   there are multiple query string parameters with the same name. Mimic that behavior here.
                                // For consistency, use the casing of the property name.
                                nonDefaultUriBindableArgNamesValues.Add(new KeyValuePair <string, string>(pubInstProp.Name, matchingQsParams[0].Value));
                            }
                            else
                            {
                                // Punt. We don't have anywhere in the current request from which to grab a value, so take the default value
                                //   of the matching property on the instance we created above.
                                nonDefaultUriBindableArgNamesValues.Add(new KeyValuePair <string, string>(pubInstProp.Name, pubInstProp.GetValue(objInstance).GetValueAsString()));
                            }
                        }
                    }
                }
            }

            // Combine default URI-bindable arg names/values with non-default URI-bindable arg names/values.
            //TODO: look for and combine args with same name from default and non-default?
            var allArgNameValues = defaultUriBindableArgNamesValues
                                   .Concat(nonDefaultUriBindableArgNamesValues)
                                   .OrderBy(kvp => kvp.Key)
                                   .ToList();

            // Get the versions for the controller/action, and for each argument name/value.
            var cacheConfig = actionContext.Request.GetConfiguration().GetOutputCacheConfiguration();
            var controllerActionVersionId = await GetControllerActionVersionIdAsync(cache, controllerLowered, actionLowered, cacheConfig.IsLocalCachingEnabled);

            var finalList = new List <string>();

            foreach (var argNameValue in allArgNameValues)
            {
                var argNameLowered = argNameValue.Key.ToLower();

                // Get or create the argument name/value version from redis. It is scoped at the namespace/controller/action level, so
                //   it will be unique.
                var key     = CacheKey.ControllerActionArgumentVersion(controllerLowered, actionLowered, argNameLowered, argNameValue.Value);
                var version = await cache.GetOrIncrAsync(key, cacheConfig.IsLocalCachingEnabled);

                finalList.Add(CacheKey.VersionedArgumentNameAndValue(argNameLowered, argNameValue.Value?.Trim(), version));
            }

            var parameters = $"-{string.Join("&", finalList)}";

            if (parameters == "-")
            {
                parameters = string.Empty;
            }

            return($"{controllerLowered}-{actionLowered}_v{controllerActionVersionId}{parameters}{MediaTypeSeparator}{mediaType}");
        }
示例#9
0
 private async Task <long> GetControllerActionVersionIdAsync(IApiOutputCache cache, string controllerLowered, string actionLowered, bool localCacheEnabled)
 {
     return(await cache.GetOrIncrAsync(CacheKey.ControllerActionVersion(controllerLowered, actionLowered), localCacheEnabled));
 }