This repository has been archived by the owner on Dec 14, 2018. It is now read-only.
/
ResponseCacheFilter.cs
161 lines (143 loc) · 5.72 KB
/
ResponseCacheFilter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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 System.Globalization;
using System.Linq;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An <see cref="ActionFilterAttribute"/> which sets the appropriate headers related to response caching.
/// </summary>
public class ResponseCacheFilter : IActionFilter, IResponseCacheFilter
{
private readonly CacheProfile _cacheProfile;
private int? _cacheDuration;
private ResponseCacheLocation? _cacheLocation;
private bool? _cacheNoStore;
private string _cacheVaryByHeader;
/// <summary>
/// Creates a new instance of <see cref="ResponseCacheFilter"/>
/// </summary>
/// <param name="cacheProfile">The profile which contains the settings for
/// <see cref="ResponseCacheFilter"/>.</param>
public ResponseCacheFilter(CacheProfile cacheProfile)
{
_cacheProfile = cacheProfile;
}
/// <summary>
/// Gets or sets the duration in seconds for which the response is cached.
/// This is a required parameter.
/// This sets "max-age" in "Cache-control" header.
/// </summary>
public int Duration
{
get { return (_cacheDuration ?? _cacheProfile.Duration) ?? 0; }
set { _cacheDuration = value; }
}
/// <summary>
/// Gets or sets the location where the data from a particular URL must be cached.
/// </summary>
public ResponseCacheLocation Location
{
get { return (_cacheLocation ?? _cacheProfile.Location) ?? ResponseCacheLocation.Any; }
set { _cacheLocation = value; }
}
/// <summary>
/// Gets or sets the value which determines whether the data should be stored or not.
/// When set to <see langword="true"/>, it sets "Cache-control" header to "no-store".
/// Ignores the "Location" parameter for values other than "None".
/// Ignores the "duration" parameter.
/// </summary>
public bool NoStore
{
get { return (_cacheNoStore ?? _cacheProfile.NoStore) ?? false; }
set { _cacheNoStore = value; }
}
/// <summary>
/// Gets or sets the value for the Vary response header.
/// </summary>
public string VaryByHeader
{
get { return _cacheVaryByHeader ?? _cacheProfile.VaryByHeader; }
set { _cacheVaryByHeader = value; }
}
// <inheritdoc />
public void OnActionExecuting([NotNull] ActionExecutingContext context)
{
// If there are more filters which can override the values written by this filter,
// then skip execution of this filter.
if (IsOverridden(context))
{
return;
}
if (!NoStore)
{
// Duration MUST be set (either in the cache profile or in this filter) unless NoStore is true.
if (_cacheProfile.Duration == null && _cacheDuration == null)
{
throw new InvalidOperationException(
Resources.FormatResponseCache_SpecifyDuration(nameof(NoStore), nameof(Duration)));
}
}
var headers = context.HttpContext.Response.Headers;
// Clear all headers
headers.Remove("Vary");
headers.Remove("Cache-control");
headers.Remove("Pragma");
if (!string.IsNullOrEmpty(VaryByHeader))
{
headers.Set("Vary", VaryByHeader);
}
if (NoStore)
{
headers.Set("Cache-control", "no-store");
// Cache-control: no-store, no-cache is valid.
if (Location == ResponseCacheLocation.None)
{
headers.Append("Cache-control", "no-cache");
headers.Set("Pragma", "no-cache");
}
}
else
{
string cacheControlValue = null;
switch (Location)
{
case ResponseCacheLocation.Any:
cacheControlValue = "public";
break;
case ResponseCacheLocation.Client:
cacheControlValue = "private";
break;
case ResponseCacheLocation.None:
cacheControlValue = "no-cache";
headers.Set("Pragma", "no-cache");
break;
}
cacheControlValue = string.Format(
CultureInfo.InvariantCulture,
"{0}{1}max-age={2}",
cacheControlValue,
cacheControlValue != null? "," : null,
Duration);
if (cacheControlValue != null)
{
headers.Set("Cache-control", cacheControlValue);
}
}
}
// <inheritdoc />
public void OnActionExecuted([NotNull]ActionExecutedContext context)
{
}
// internal for Unit Testing purposes.
internal bool IsOverridden([NotNull] ActionExecutingContext context)
{
// Return true if there are any filters which are after the current filter. In which case the current
// filter should be skipped.
return context.Filters.OfType<IResponseCacheFilter>().Last() != this;
}
}
}