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