Changes introducing ResponseCache to cache content in the client/proxy.

This commit is contained in:
sornaks 2015-01-05 19:23:52 -08:00 committed by Harsh Gupta
parent d2fe1ebad7
commit 262bb9a732
16 changed files with 792 additions and 1 deletions

17
Mvc.sln
View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22410.0
VisualStudioVersion = 14.0.22416.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -118,6 +118,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ErrorPageMiddlewareWebSite"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CustomRouteWebSite", "test\WebSites\CustomRouteWebSite\CustomRouteWebSite.kproj", "{364EC3C6-C9DB-45E0-A0F2-1EE61E4B429B}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ResponseCacheWebSite", "test\WebSites\ResponseCacheWebSite\ResponseCacheWebSite.kproj", "{BDEEBE09-C0C4-433C-B0B8-8478C9776996}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -654,6 +656,18 @@ Global
{364EC3C6-C9DB-45E0-A0F2-1EE61E4B429B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{364EC3C6-C9DB-45E0-A0F2-1EE61E4B429B}.Release|x86.ActiveCfg = Release|Any CPU
{364EC3C6-C9DB-45E0-A0F2-1EE61E4B429B}.Release|x86.Build.0 = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|x86.ActiveCfg = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Debug|x86.Build.0 = Debug|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|Any CPU.Build.0 = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|x86.ActiveCfg = Release|Any CPU
{BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -711,5 +725,6 @@ Global
{0AD78AB5-D67C-49BC-81B1-0C51BFA82B5E} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{AD545A5B-2BA5-4314-88AC-FC2ACF2CC718} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{364EC3C6-C9DB-45E0-A0F2-1EE61E4B429B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{BDEEBE09-C0C4-433C-B0B8-8478C9776996} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An <see cref="IActionFilter"/> which sets the appropriate headers related to Response caching.
/// </summary>
public interface IResponseCacheFilter : IActionFilter
{
}
}

View File

@ -0,0 +1,134 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An <see cref="ActionFilterAttribute"/> which sets the appropriate headers related to Response caching.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ResponseCacheAttribute : ActionFilterAttribute, IResponseCacheFilter
{
// A nullable-int cannot be used as an Attribute parameter.
// Hence this nullable-int is present to back the Duration property.
private int? _duration;
/// <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 _duration ?? 0;
}
set
{
_duration = value;
}
}
/// <summary>
/// Gets or sets the location where the data from a particular URL must be cached.
/// </summary>
public ResponseCacheLocation Location { get; set; }
/// <summary>
/// Gets or sets the value which determines whether the data should be stored or not.
/// When set to 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; set; }
/// <summary>
/// Gets or sets the value for the Vary response header.
/// </summary>
public string VaryByHeader { get; set; }
// <inheritdoc />
public override 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;
}
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
{
if (_duration == null)
{
throw new InvalidOperationException(
Resources.FormatResponseCache_SpecifyDuration(nameof(NoStore), nameof(Duration)));
}
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);
}
}
}
// 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;
}
}
}

View File

@ -1546,6 +1546,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("AsyncResourceFilter_InvalidShortCircuit"), p0, p1, p2, p3);
}
/// <summary>
/// If the '{0}' property is not set to true, '{1}' property must be specified.
/// </summary>
internal static string ResponseCache_SpecifyDuration
{
get { return GetString("ResponseCache_SpecifyDuration"); }
}
/// <summary>
/// If the '{0}' property is not set to true, '{1}' property must be specified.
/// </summary>
internal static string FormatResponseCache_SpecifyDuration(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ResponseCache_SpecifyDuration"), p0, p1);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -415,4 +415,7 @@
<data name="AsyncResourceFilter_InvalidShortCircuit" xml:space="preserve">
<value>If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.</value>
</data>
<data name="ResponseCache_SpecifyDuration" xml:space="preserve">
<value>If the '{0}' property is not set to true, '{1}' property must be specified.</value>
</data>
</root>

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Determines the value for the "Cache-control" header in the response.
/// </summary>
public enum ResponseCacheLocation
{
/// <summary>
/// Cached in both proxies and client.
/// Sets "Cache-control" header to "public".
/// </summary>
Any = 0,
/// <summary>
/// Cached only in the client.
/// Sets "Cache-control" header to "private".
/// </summary>
Client = 1,
/// <summary>
/// "Cache-control" and "Pragma" headers are set to "no-cache".
/// </summary>
None = 2
}
}

View File

@ -0,0 +1,242 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Collections.Generic;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class ResponseCacheAttributeTest
{
[Fact]
public void OnActionExecuting_DoesNotThrow_WhenNoStoreIsTrue()
{
// Arrange
var cache = new ResponseCacheAttribute() { NoStore = true };
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act
cache.OnActionExecuting(context);
// Assert
Assert.Equal("no-store", context.HttpContext.Response.Headers.Get("Cache-control"));
}
[Fact]
public void OnActionExecuting_ThrowsIfDurationIsNotSet_WhenNoStoreIsFalse()
{
// Arrange
var cache = new ResponseCacheAttribute() { NoStore = false };
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => { cache.OnActionExecuting(context); });
Assert.Equal("If the 'NoStore' property is not set to true, 'Duration' property must be specified.",
exception.Message);
}
public static IEnumerable<object[]> CacheControlData
{
get
{
yield return new object[] { new ResponseCacheAttribute { NoStore = true, Duration = 0 }, "no-store" };
// If no-store is set, then location is ignored.
yield return new object[] {
new ResponseCacheAttribute
{ NoStore = true, Duration = 0, Location = ResponseCacheLocation.Client },
"no-store"
};
yield return new object[] {
new ResponseCacheAttribute { NoStore = true, Duration = 0, Location = ResponseCacheLocation.Any },
"no-store"
};
// If no-store is set, then duration is ignored.
yield return new object[] {
new ResponseCacheAttribute { NoStore = true, Duration = 100 }, "no-store"
};
yield return new object[] {
new ResponseCacheAttribute { Location = ResponseCacheLocation.Client, Duration = 10 },
"private,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute { Location = ResponseCacheLocation.Any, Duration = 10 },
"public,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute { Location = ResponseCacheLocation.None, Duration = 10 },
"no-cache,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute { Location = ResponseCacheLocation.Client, Duration = 10 },
"private,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute { Location = ResponseCacheLocation.Any, Duration = 31536000 },
"public,max-age=31536000"
};
yield return new object[] {
new ResponseCacheAttribute { Duration = 20 },
"public,max-age=20"
};
}
}
[Theory]
[MemberData(nameof(CacheControlData))]
public void OnActionExecuting_CanSetCacheControlHeaders(ResponseCacheAttribute cache, string output)
{
// Arrange
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act
cache.OnActionExecuting(context);
// Assert
Assert.Equal(output, context.HttpContext.Response.Headers.Get("Cache-control"));
}
public static IEnumerable<object[]> NoStoreData
{
get
{
// If no-store is set, then location is ignored.
yield return new object[] {
new ResponseCacheAttribute
{ NoStore = true, Location = ResponseCacheLocation.Client, Duration = 0 },
"no-store"
};
yield return new object[] {
new ResponseCacheAttribute { NoStore = true, Location = ResponseCacheLocation.Any, Duration = 0 },
"no-store"
};
// If no-store is set, then duration is ignored.
yield return new object[] {
new ResponseCacheAttribute { NoStore = true, Duration = 100 }, "no-store"
};
}
}
[Theory]
[MemberData(nameof(NoStoreData))]
public void OnActionExecuting_DoesNotSetLocationOrDuration_IfNoStoreIsSet(
ResponseCacheAttribute cache, string output)
{
// Arrange
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act
cache.OnActionExecuting(context);
// Assert
Assert.Equal(output, context.HttpContext.Response.Headers.Get("Cache-control"));
}
public static IEnumerable<object[]> VaryData
{
get
{
yield return new object[] {
new ResponseCacheAttribute { VaryByHeader = "Accept", Duration = 10 },
"Accept",
"public,max-age=10" };
yield return new object[] {
new ResponseCacheAttribute { VaryByHeader = "Accept", NoStore = true, Duration = 0 },
"Accept",
"no-store"
};
yield return new object[] {
new ResponseCacheAttribute {
Location = ResponseCacheLocation.Client, Duration = 10, VaryByHeader = "Accept"
},
"Accept",
"private,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute {
Location = ResponseCacheLocation.Any, Duration = 10, VaryByHeader = "Test"
},
"Test",
"public,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute {
Location = ResponseCacheLocation.Client, Duration = 10, VaryByHeader = "Test"
},
"Test",
"private,max-age=10"
};
yield return new object[] {
new ResponseCacheAttribute {
Location = ResponseCacheLocation.Any,
Duration = 31536000,
VaryByHeader = "Test"
},
"Test",
"public,max-age=31536000"
};
}
}
[Theory]
[MemberData(nameof(VaryData))]
public void ResponseCacheCanSetVary(ResponseCacheAttribute cache, string varyOutput, string cacheControlOutput)
{
// Arrange
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act
cache.OnActionExecuting(context);
// Assert
Assert.Equal(varyOutput, context.HttpContext.Response.Headers.Get("Vary"));
Assert.Equal(cacheControlOutput, context.HttpContext.Response.Headers.Get("Cache-control"));
}
[Fact]
public void SetsPragmaOnNoCache()
{
// Arrange
var cache = new ResponseCacheAttribute()
{
NoStore = true,
Location = ResponseCacheLocation.None,
Duration = 0
};
var context = GetActionExecutingContext(new List<IFilter> { cache });
// Act
cache.OnActionExecuting(context);
// Assert
Assert.Equal("no-store,no-cache", context.HttpContext.Response.Headers.Get("Cache-control"));
Assert.Equal("no-cache", context.HttpContext.Response.Headers.Get("Pragma"));
}
[Fact]
public void IsOverridden_ReturnsTrueForAllButLastFilter()
{
// Arrange
var caches = new List<IFilter>();
caches.Add(new ResponseCacheAttribute());
caches.Add(new ResponseCacheAttribute());
var context = GetActionExecutingContext(caches);
// Act & Assert
Assert.True((caches[0] as ResponseCacheAttribute).IsOverridden(context));
Assert.False((caches[1] as ResponseCacheAttribute).IsOverridden(context));
}
private ActionExecutingContext GetActionExecutingContext(List<IFilter> filters = null)
{
return new ActionExecutingContext(
new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()),
filters ?? new List<IFilter>(),
new Dictionary<string, object>());
}
}
}

View File

@ -0,0 +1,161 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using ResponseCacheWebSite;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class ResponseCacheTest
{
private readonly IServiceProvider _provider = TestHelper.CreateServices("ResponseCacheWebSite");
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
[Fact]
public async Task ResponseCache_SetsAllHeaders()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/CacheHeaders/Index");
// Assert
var data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=100", data);
data = Assert.Single(response.Headers.GetValues("Vary"));
Assert.Equal("Accept", data);
}
public static IEnumerable<object[]> CacheControlData
{
get
{
yield return new object[] { "http://localhost/CacheHeaders/PublicCache", "public, max-age=100" };
yield return new object[] { "http://localhost/CacheHeaders/ClientCache", "max-age=100, private" };
yield return new object[] { "http://localhost/CacheHeaders/NoStore", "no-store" };
yield return new object[] { "http://localhost/CacheHeaders/NoCacheAtAll", "no-store, no-cache" };
}
}
[Theory]
[MemberData(nameof(CacheControlData))]
public async Task ResponseCache_SetsDifferentCacheControlHeaders(string url, string expected)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
var data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal(expected, data);
}
[Fact]
public async Task SetsHeadersForAllActionsOfClass()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response1 = await client.GetAsync("http://localhost/ClassLevelCache/GetHelloWorld");
var response2 = await client.GetAsync("http://localhost/ClassLevelCache/GetFooBar");
// Assert
var data = Assert.Single(response1.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=100", data);
data = Assert.Single(response1.Headers.GetValues("Vary"));
Assert.Equal("Accept", data);
data = Assert.Single(response2.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=100", data);
data = Assert.Single(response2.Headers.GetValues("Vary"));
Assert.Equal("Accept", data);
}
[Fact]
public async Task HeadersSetInActionOverridesTheOnesInClass()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/ClassLevelCache/ConflictExistingHeader");
// Assert
var data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=20", data);
}
[Fact]
public async Task HeadersToNotCacheAParticularAction()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/ClassLevelCache/DoNotCacheThisAction");
// Assert
var data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal("no-store, no-cache", data);
}
[Fact]
public async Task ClassLevelHeadersAreUnsetByActionLevelHeaders()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/ClassLevelNoStore/CacheThisAction");
// Assert
var data = Assert.Single(response.Headers.GetValues("Vary"));
Assert.Equal("Accept", data);
data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=10", data);
Assert.Throws<InvalidOperationException>(() => response.Headers.GetValues("Pragma"));
}
[Fact]
public async Task SetsCacheControlPublicByDefault()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/CacheHeaders/SetsCacheControlPublicByDefault");
// Assert
var data = Assert.Single(response.Headers.GetValues("Cache-control"));
Assert.Equal("public, max-age=40", data);
}
[Fact]
public async Task ThrowsWhenDurationIsNotSet()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(
() => client.GetAsync("http://localhost/CacheHeaders/ThrowsWhenDurationIsNotSet"));
}
}
}

View File

@ -27,6 +27,7 @@
"RazorInstrumentationWebsite": "1.0.0",
"RazorViewEngineOptionsWebsite": "1.0.0",
"RequestServicesWebSite": "1.0.0",
"ResponseCacheWebSite": "1.0.0",
"TagHelperSample.Web": "1.0.0",
"TagHelpersWebSite": "1.0.0",
"UrlHelperWebSite": "1.0.0",

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ResponseCacheWebSite
{
public class CacheHeadersController : Controller
{
[ResponseCache(Duration = 100, Location = ResponseCacheLocation.Any, VaryByHeader = "Accept")]
public IActionResult Index()
{
return Content("Hello World!");
}
[ResponseCache(Duration = 100, Location = ResponseCacheLocation.Any)]
public IActionResult PublicCache()
{
return Content("Hello World!");
}
[ResponseCache(Duration = 100, Location = ResponseCacheLocation.Client)]
public IActionResult ClientCache()
{
return Content("Hello World!");
}
[ResponseCache(NoStore = true, Duration = 0)]
public IActionResult NoStore()
{
return Content("Hello World!");
}
[ResponseCache(NoStore = true, Duration = 0, Location = ResponseCacheLocation.None)]
public IActionResult NoCacheAtAll()
{
return Content("Hello World!");
}
[ResponseCache(Duration = 40)]
public IActionResult SetHeadersInAction()
{
Response.Headers.Set("Cache-control", "max-age=10");
return Content("Hello World!");
}
[ResponseCache(Duration = 40)]
public IActionResult SetsCacheControlPublicByDefault()
{
return Content("Hello World!");
}
[ResponseCache(VaryByHeader = "Accept")]
public IActionResult ThrowsWhenDurationIsNotSet()
{
return Content("Hello World!");
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ResponseCacheWebSite
{
[ResponseCache(Duration = 100, Location = ResponseCacheLocation.Any, VaryByHeader = "Accept")]
public class ClassLevelCacheController
{
public string GetHelloWorld()
{
return "Hello, World!";
}
public string GetFooBar()
{
return "Foo Bar!";
}
[ResponseCache(Duration = 20)]
public string ConflictExistingHeader()
{
return "Conflict";
}
[ResponseCache(NoStore = true, Duration = 0, Location = ResponseCacheLocation.None)]
public string DoNotCacheThisAction()
{
return "Conflict";
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ResponseCacheWebSite
{
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None, Duration = 0)]
public class ClassLevelNoStoreController
{
public string GetHelloWorld()
{
return "Hello, World!";
}
[ResponseCache(VaryByHeader = "Accept", Duration = 10)]
public string CacheThisAction()
{
return "Conflict";
}
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>bdeebe09-c0c4-433c-b0b8-8478c9776996</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>63072</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
namespace ResponseCacheWebSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var configuration = app.GetTestConfiguration();
app.UseServices(services =>
{
services.AddMvc(configuration);
});
app.UseMvc();
}
}
}

View File

@ -0,0 +1,19 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
},
"webroot": "wwwroot"
}

Binary file not shown.