// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc { /// /// Specifies the parameters necessary for setting appropriate headers in response caching. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class ResponseCacheAttribute : Attribute, IFilterFactory, IOrderedFilter { // A nullable-int cannot be used as an Attribute parameter. // Hence this nullable-int is present to back the Duration property. // The same goes for nullable-ResponseCacheLocation and nullable-bool. private int? _duration; private ResponseCacheLocation? _location; private bool? _noStore; /// /// Gets or sets the duration in seconds for which the response is cached. /// This sets "max-age" in "Cache-control" header. /// public int Duration { get { return _duration ?? 0; } set { _duration = value; } } /// /// Gets or sets the location where the data from a particular URL must be cached. /// public ResponseCacheLocation Location { get { return _location ?? ResponseCacheLocation.Any; } set { _location = value; } } /// /// Gets or sets the value which determines whether the data should be stored or not. /// When set to , it sets "Cache-control" header to "no-store". /// Ignores the "Location" parameter for values other than "None". /// Ignores the "duration" parameter. /// public bool NoStore { get { return _noStore ?? false; } set { _noStore = value; } } /// /// Gets or sets the value for the Vary response header. /// public string VaryByHeader { get; set; } /// /// Gets or sets the value of the cache profile name. /// public string CacheProfileName { get; set; } /// public int Order { get; set; } /// public bool IsReusable => true; /// public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { if (serviceProvider == null) { throw new ArgumentNullException(nameof(serviceProvider)); } var optionsAccessor = serviceProvider.GetRequiredService>(); CacheProfile selectedProfile = null; if (CacheProfileName != null) { optionsAccessor.Value.CacheProfiles.TryGetValue(CacheProfileName, out selectedProfile); if (selectedProfile == null) { throw new InvalidOperationException(Resources.FormatCacheProfileNotFound(CacheProfileName)); } } // If the ResponseCacheAttribute parameters are set, // then it must override the values from the Cache Profile. // The below expression first checks if the duration is set by the attribute's parameter. // If absent, it checks the selected cache profile (Note: There can be no cache profile as well) // The same is the case for other properties. _duration = _duration ?? selectedProfile?.Duration; _noStore = _noStore ?? selectedProfile?.NoStore; _location = _location ?? selectedProfile?.Location; VaryByHeader = VaryByHeader ?? selectedProfile?.VaryByHeader; // ResponseCacheFilter cannot take any null values. Hence, if there are any null values, // the properties convert them to their defaults and are passed on. return new ResponseCacheFilter(new CacheProfile { Duration = _duration, Location = _location, NoStore = _noStore, VaryByHeader = VaryByHeader }); } } }