From 8eea0ad44c4d469abc2c2d52f1c79505bd6afaeb Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 14 Aug 2018 16:13:17 -0700 Subject: [PATCH 1/3] Update tests to latest compat switch --- samples/MvcSandbox/Startup.cs | 2 +- .../Internal/MvcEndpointDataSource.cs | 15 +--- .../Internal/MvcEndpointDataSourceTests.cs | 4 +- .../ApiExplorerTest.cs | 2 +- .../ApplicationModelTest.cs | 2 +- .../CorsEndpointRoutingTests.cs | 25 +------ .../CorsTests.cs | 25 ++++++- .../CorsTestsBase.cs | 2 +- .../EndpointRoutingTest.cs | 39 +--------- .../GlobalAuthorizationFilterTest.cs | 10 +-- .../HtmlGenerationTest.cs | 40 +++++++++- .../Infrastructure/MvcTestFixture.cs | 4 - .../RazorPagesTest.cs | 2 +- .../RoutingTests.cs | 61 +++++++++++++++- .../RoutingTestsBase.cs | 49 ++----------- .../VersioningEndpointRoutingTests.cs | 4 +- .../VersioningTests.cs | 4 +- ...ite.HtmlGeneration_Home.Index.Encoded.html | 14 ++-- ...tionWebSite.HtmlGeneration_Home.Index.html | 14 ++-- ...ite.HtmlGeneration_Home.Index21Compat.html | 73 +++++++++++++++++++ .../TagHelpersWebSite.Home.About.html | 4 +- .../TagHelpersWebSite.Home.Help.html | 4 +- .../TagHelpersWebSite.Home.Index.html | 4 +- ...sWebSite.Home.ViewComponentTagHelpers.html | 4 +- .../ActionDescriptorChangeProvider.cs | 18 +++-- .../ApiExplorerRouteChangeConvention.cs | 35 +++++++++ .../ApiExplorerReloadableController.cs | 28 +------ .../ApiExplorerWebSite/ReloadAttribute.cs | 8 ++ test/WebSites/ApiExplorerWebSite/Startup.cs | 11 ++- .../WellKnownChangeToken.cs | 12 +++ .../ApplicationModelWebSite/Startup.cs | 4 +- .../BasicWebSite/StartupRequestLimitSize.cs | 4 +- ...hCookieTempDataProviderAndCookieConsent.cs | 4 +- .../StartupWithSessionTempDataProvider.cs | 4 +- .../ControllersFromServicesWebSite/Startup.cs | 5 +- test/WebSites/CorsWebSite/Startup.cs | 6 +- ...pointRouting.cs => StartupWith21Compat.cs} | 8 +- .../ErrorPageMiddlewareWebSite/Startup.cs | 4 +- test/WebSites/FilesWebSite/Startup.cs | 4 +- .../WebSites/HtmlGenerationWebSite/Startup.cs | 5 +- .../StartupWith21CompatibilityBehavior.cs | 55 ++++++++++++++ .../StartupWithCultureReplace.cs | 1 - .../Views/HtmlGeneration_Home/Index.cshtml | 4 +- test/WebSites/RazorBuildWebSite/Startup.cs | 4 +- test/WebSites/RazorPagesWebSite/Startup.cs | 4 +- .../RazorPagesWebSite/StartupWithBasePath.cs | 4 +- .../Controllers/EmbeddedViewsController.cs | 2 + test/WebSites/RazorWebSite/Startup.cs | 4 +- .../RazorWebSite/StartupDataAnnotations.cs | 5 +- test/WebSites/RoutingWebSite/Startup.cs | 12 ++- ...pointRouting.cs => StartupWith21Compat.cs} | 5 +- test/WebSites/SecurityWebSite/Startup.cs | 4 +- ...ith20CompatAndGlobalDenyAnonymousFilter.cs | 41 +++++++++++ .../StartupWithGlobalDenyAnonymousFilter.cs | 4 +- test/WebSites/SimpleWebSite/Startup.cs | 6 +- test/WebSites/TagHelpersWebSite/Startup.cs | 4 +- test/WebSites/VersioningWebSite/Startup.cs | 6 +- ...pointRouting.cs => StartupWith21Compat.cs} | 7 +- .../WebApiCompatShimWebSite/Startup.cs | 6 +- test/WebSites/XmlFormattersWebSite/Startup.cs | 3 +- 60 files changed, 495 insertions(+), 253 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index21Compat.html create mode 100644 test/WebSites/ApiExplorerWebSite/ApiExplorerRouteChangeConvention.cs create mode 100644 test/WebSites/ApiExplorerWebSite/ReloadAttribute.cs create mode 100644 test/WebSites/ApiExplorerWebSite/WellKnownChangeToken.cs rename test/WebSites/CorsWebSite/{StartupWithEndpointRouting.cs => StartupWith21Compat.cs} (93%) create mode 100644 test/WebSites/HtmlGenerationWebSite/StartupWith21CompatibilityBehavior.cs rename test/WebSites/RoutingWebSite/{StartupWithEndpointRouting.cs => StartupWith21Compat.cs} (90%) create mode 100644 test/WebSites/SecurityWebSite/StartupWith20CompatAndGlobalDenyAnonymousFilter.cs rename test/WebSites/VersioningWebSite/{StartupWithEndpointRouting.cs => StartupWith21Compat.cs} (81%) diff --git a/samples/MvcSandbox/Startup.cs b/samples/MvcSandbox/Startup.cs index 0b3360ec8a..d9f96bf08b 100644 --- a/samples/MvcSandbox/Startup.cs +++ b/samples/MvcSandbox/Startup.cs @@ -14,7 +14,7 @@ namespace MvcSandbox // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_2); + services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Latest); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs index 4150ed26f3..128e699774 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Mvc.Internal @@ -59,10 +60,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal _httpContextInstance = new DefaultHttpContext() { RequestServices = serviceProvider }; ConventionalEndpointInfos = new List(); - - Extensions.Primitives.ChangeToken.OnChange( - GetCompositeChangeToken, - UpdateEndpoints); } private List CreateEndpoints() @@ -454,7 +451,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal return new CompositeChangeToken(changeTokens); } - public override IChangeToken GetChangeToken() => GetCompositeChangeToken(); + public override IChangeToken GetChangeToken() => NullChangeToken.Singleton; public override IReadOnlyList Endpoints { @@ -479,14 +476,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal } } - private void UpdateEndpoints() - { - lock (_lock) - { - _endpoints = CreateEndpoints(); - } - } - public List ConventionalEndpointInfos { get; } private class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata { } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs index 55fd51e0b8..fbee5e6f55 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs @@ -133,7 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal Assert.True(actionInvokerCalled); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Routing/issues/722")] public void GetChangeToken_MultipleChangeTokenProviders_ComposedResult() { // Arrange @@ -287,7 +287,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal actionDescriptorCollectionProviderMock.VerifyGet(m => m.ActionDescriptors, Times.Once); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Routing/issues/722")] public void Endpoints_ChangeTokenTriggered_EndpointsRecreated() { // Arrange diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiExplorerTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiExplorerTest.cs index d216ad4aff..40e556ad72 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiExplorerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiExplorerTest.cs @@ -1087,7 +1087,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests }); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Routing/issues/722")] public async Task ApiExplorer_Updates_WhenActionDescriptorCollectionIsUpdated() { // Act - 1 diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApplicationModelTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApplicationModelTest.cs index a698af7ba4..dcb50e1d8e 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApplicationModelTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApplicationModelTest.cs @@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("From Header - HelloWorld", body); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Routing/issues/721")] public async Task ActionModelSuppressedForPathMatching_CannotBeRouted() { // Arrange & Act diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsEndpointRoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsEndpointRoutingTests.cs index 44402ff200..82867e6ed5 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsEndpointRoutingTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsEndpointRoutingTests.cs @@ -1,34 +1,13 @@ // 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.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Cors.Infrastructure; -using Xunit; - namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class CorsEndpointRoutingTests : CorsTestsBase + public class CorsEndpointRoutingTests : CorsTestsBase { - public CorsEndpointRoutingTests(MvcTestFixture fixture) + public CorsEndpointRoutingTests(MvcTestFixture fixture) : base(fixture) { } - - [Fact] // This intentionally returns a 405 with endpoint routing - public override async Task PreflightRequestOnNonCorsEnabledController_DoesNotMatchTheAction() - { - // Arrange - var request = new HttpRequestMessage(new HttpMethod("OPTIONS"), "http://localhost/NonCors/Post"); - request.Headers.Add(CorsConstants.Origin, "http://example.com"); - request.Headers.Add(CorsConstants.AccessControlRequestMethod, "POST"); - - // Act - var response = await Client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); } - } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTests.cs index 4d241ddeb0..512fc5ea90 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTests.cs @@ -1,13 +1,34 @@ // 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.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Xunit; + namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class CorsTests : CorsTestsBase + public class CorsTests : CorsTestsBase { - public CorsTests(MvcTestFixture fixture) + public CorsTests(MvcTestFixture fixture) : base(fixture) { } + + [Fact] + public override async Task PreflightRequestOnNonCorsEnabledController_DoesNotMatchTheAction() + { + // Arrange + var request = new HttpRequestMessage(new HttpMethod("OPTIONS"), "http://localhost/NonCors/Post"); + request.Headers.Add(CorsConstants.Origin, "http://example.com"); + request.Headers.Add(CorsConstants.AccessControlRequestMethod, "POST"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTestsBase.cs index 899ce4e792..01e9e900eb 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTestsBase.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/CorsTestsBase.cs @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests var response = await Client.SendAsync(request); // Assert - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); } [Theory] diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/EndpointRoutingTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/EndpointRoutingTest.cs index 81a21798eb..f82be33703 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/EndpointRoutingTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/EndpointRoutingTest.cs @@ -10,9 +10,9 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class EndpointRoutingTest : RoutingTestsBase + public class EndpointRoutingTest : RoutingTestsBase { - public EndpointRoutingTest(MvcTestFixture fixture) + public EndpointRoutingTest(MvcTestFixture fixture) : base(fixture) { } @@ -32,26 +32,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.True(result); } - [Fact] - public override Task AttributeRoutedAction_InArea_StaysInArea_ActionDoesntExist() - { - // By design, this test cannot work in EndpointRouting world. This is because in case of old routing test - // when a link generation to an attribute routed controller with a non-existing action does not succeeed, - // the next route in the route collection is considered and since the next route in the route collection is - // a conventional area route, the old routing test succeeds. But this cannot happen in case of endpoint - // routing as the action does not exist to begin with. - return Task.CompletedTask; - } - - [Fact] - public override Task ConventionalRoutedAction_InArea_StaysInArea() - { - // By design, this test cannot work in EndpointRouting world. In old routing test a link is being generated - // to a non-existing action on a controller which is in an area. In case of endpoint routing, we cannot - // generate links as the action does not exist to begin with. - return Task.CompletedTask; - } - [Fact] public async override Task RouteData_Routers_ConventionalRoute() { @@ -97,21 +77,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); } - // Endpoint routing exposes HTTP 405s for HTTP method mismatches - [Fact] - public override async Task AttributeRoutedAction_MultipleRouteAttributes_RouteAttributeTemplatesIgnoredForOverrideActions() - { - // Arrange - var url = "http://localhost/api/v1/Maps"; - - // Act - var response = await Client.SendAsync(new HttpRequestMessage(new HttpMethod("POST"), url)); - - // Assert - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - } - - // Endpoint routing exposes HTTP 405s for HTTP method mismatches [Theory] [MemberData(nameof(AttributeRoutedAction_MultipleRouteAttributes_WithMultipleHttpAttributes_RespectsConstraintsData))] public override async Task AttributeRoutedAction_MultipleRouteAttributes_WithMultipleHttpAttributes_RespectsConstraints( diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalAuthorizationFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalAuthorizationFilterTest.cs index 23dce2a053..5ac55864af 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalAuthorizationFilterTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalAuthorizationFilterTest.cs @@ -6,9 +6,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -58,11 +56,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public async Task AuthorizationPoliciesDoNotCombine_WithV2_0() { // Arrange & Act - var factory = Factory.WithWebHostBuilder( - builder => builder.ConfigureServices( - services => services.Configure( - options => options.CompatibilityVersion = CompatibilityVersion.Version_2_0))); - var client = factory.CreateDefaultClient(); + var client = Factory + .WithWebHostBuilder(builder => builder.UseStartup()) + .CreateDefaultClient(); var response = await client.PostAsync("http://localhost/Administration/SignInCookie2", null); // Assert diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/HtmlGenerationTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/HtmlGenerationTest.cs index 8c7984814e..65ba3292dd 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/HtmlGenerationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/HtmlGenerationTest.cs @@ -10,8 +10,8 @@ using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Threading.Tasks; -using AngleSharp.Dom; -using AngleSharp.Dom.Html; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -26,14 +26,21 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests MvcTestFixture fixture, MvcEncodedTestFixture encodedFixture) { + Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); + Client = fixture.CreateDefaultClient(); EncodedClient = encodedFixture.CreateDefaultClient(); } + private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => + builder.UseStartup(); + public HttpClient Client { get; } public HttpClient EncodedClient { get; } + public WebApplicationFactory Factory { get; } + public static TheoryData WebPagesData { get @@ -131,6 +138,35 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests } } + [Fact] + public async Task HtmlGenerationWebSite_LinkGeneration_With21CompatibilityBehavior() + { + // Arrange + var client = Factory + .WithWebHostBuilder(builder => builder.UseStartup()) + .CreateDefaultClient(); + var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8"); + var outputFile = "compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index21Compat.html"; + var expectedContent = + await ResourceFile.ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false); + + // Act + // The host is not important as everything runs in memory and tests are isolated from each other. + var response = await client.GetAsync("http://localhost/HtmlGeneration_Home/"); + var responseContent = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); + + responseContent = responseContent.Trim(); +#if GENERATE_BASELINES + ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent); +#else + Assert.Equal(expectedContent.Trim(), responseContent, ignoreLineEndingDifferences: true); +#endif + } + public static TheoryData EncodedPagesData { get diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/Infrastructure/MvcTestFixture.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/Infrastructure/MvcTestFixture.cs index a7d4a643de..cf1ed53098 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/Infrastructure/MvcTestFixture.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/Infrastructure/MvcTestFixture.cs @@ -3,7 +3,6 @@ using System.Globalization; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; @@ -26,9 +25,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests var testSink = new TestSink(); var loggerFactory = new TestLoggerFactory(testSink, enabled: true); services.AddSingleton(loggerFactory); - - services.Configure( - options => options.CompatibilityVersion = CompatibilityVersion.Version_2_1); }); } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index 88be27634f..3c90af4882 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -919,7 +919,7 @@ Hello from /Pages/WithViewStart/Index.cshtml!"; { // Arrange var expected = -@"Microsoft.AspNetCore.Mvc.Routing.UrlHelper +@"Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper`1[AspNetCore.InjectedPageProperties] Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPageProperties]"; diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs index a4fbb4ab58..538d06358c 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Routing; @@ -10,9 +11,9 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class RoutingTests : RoutingTestsBase + public class RoutingTests : RoutingTestsBase { - public RoutingTests(MvcTestFixture fixture) + public RoutingTests(MvcTestFixture fixture) : base(fixture) { } @@ -32,6 +33,62 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.False(result); } + // Legacy routing supports linking to actions that don't exist + [Fact] + public async Task AttributeRoutedAction_InArea_StaysInArea_ActionDoesntExist() + { + // Arrange + var url = LinkFrom("http://localhost/ContosoCorp/Trains") + .To(new { action = "Contact", controller = "Home", }); + + // Act + var response = await Client.GetAsync(url); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject(body); + + Assert.Equal("Rail", result.Controller); + Assert.Equal("Index", result.Action); + + Assert.Equal("/Travel/Home/Contact", result.Link); + } + + [Fact] + public async Task ConventionalRoutedAction_InArea_StaysInArea() + { + // Arrange + var url = LinkFrom("http://localhost/Travel/Flight").To(new { action = "Contact", controller = "Home", }); + + // Act + var response = await Client.GetAsync(url); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject(body); + + Assert.Equal("Flight", result.Controller); + Assert.Equal("Index", result.Action); + + Assert.Equal("/Travel/Home/Contact", result.Link); + } + + // Legacy routing returns 404 when an action does not support a HTTP method. + [Fact] + public override async Task AttributeRoutedAction_MultipleRouteAttributes_RouteAttributeTemplatesIgnoredForOverrideActions() + { + // Arrange + var url = "http://localhost/api/v1/Maps"; + + // Act + var response = await Client.SendAsync(new HttpRequestMessage(new HttpMethod("POST"), url)); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + [Fact] public async override Task RouteData_Routers_ConventionalRoute() { diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs index 4767f77696..6430ff2005 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs @@ -340,7 +340,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests var response = await Client.SendAsync(new HttpRequestMessage(new HttpMethod("POST"), url)); // Assert - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); } [Theory] @@ -1016,26 +1016,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("/", result.Link); } - [Fact] - public virtual async Task ConventionalRoutedAction_InArea_StaysInArea() - { - // Arrange - var url = LinkFrom("http://localhost/Travel/Flight").To(new { action = "Contact", controller = "Home", }); - - // Act - var response = await Client.GetAsync(url); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var body = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject(body); - - Assert.Equal("Flight", result.Controller); - Assert.Equal("Index", result.Action); - - Assert.Equal("/Travel/Home/Contact", result.Link); - } - [Fact] public virtual async Task AttributeRoutedAction_LinkToArea() { @@ -1098,26 +1078,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("/", result.Link); } - [Fact] - public virtual async Task AttributeRoutedAction_InArea_StaysInArea_ActionDoesntExist() - { - // Arrange - var url = LinkFrom("http://localhost/ContosoCorp/Trains") - .To(new { action = "Contact", controller = "Home", }); - - // Act - var response = await Client.GetAsync(url); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var body = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject(body); - - Assert.Equal("Rail", result.Controller); - Assert.Equal("Index", result.Action); - - Assert.Equal("/Travel/Home/Contact", result.Link); - } + [Fact] public virtual async Task AttributeRoutedAction_InArea_LinkToConventionalRoutedActionInArea() @@ -1280,13 +1241,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal(actionName, result.Action); } - private static LinkBuilder LinkFrom(string url) + protected static LinkBuilder LinkFrom(string url) { return new LinkBuilder(url); } // See TestResponseGenerator in RoutingWebSite for the code that generates this data. - private class RoutingResult + protected class RoutingResult { public string[] ExpectedUrls { get; set; } @@ -1303,7 +1264,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public string Link { get; set; } } - private class LinkBuilder + protected class LinkBuilder { public LinkBuilder(string url) { diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningEndpointRoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningEndpointRoutingTests.cs index 2aeb0d2490..30f2ea33ba 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningEndpointRoutingTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningEndpointRoutingTests.cs @@ -9,9 +9,9 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class VersioningEndpointRoutingTests : VersioningTestsBase + public class VersioningEndpointRoutingTests : VersioningTestsBase { - public VersioningEndpointRoutingTests(MvcTestFixture fixture) + public VersioningEndpointRoutingTests(MvcTestFixture fixture) : base(fixture) { } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs index 40e14bdc57..80e057bbd1 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs @@ -8,9 +8,9 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests { - public class VersioningTests : VersioningTestsBase + public class VersioningTests : VersioningTestsBase { - public VersioningTests(MvcTestFixture fixture) + public VersioningTests(MvcTestFixture fixture) : base(fixture) { } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html index dfed464b45..c518a8fea6 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html @@ -13,7 +13,7 @@ Default Controller
Product Submit Fragment @@ -39,32 +39,32 @@
Non-existent Area diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.html index e80ec4969e..1800bea8a1 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.html @@ -13,7 +13,7 @@ Default Controller
Product Submit Fragment @@ -39,32 +39,32 @@
Non-existent Area diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index21Compat.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index21Compat.html new file mode 100644 index 0000000000..59380d72d6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index21Compat.html @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html index e675616a89..72ba50bb99 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html @@ -12,8 +12,8 @@

ASP.NET vNext - About

| My Home - | My About - | My Help |

+ | My About + | My Help |

diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html index 0a20fbb435..a3d85388ba 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html @@ -12,8 +12,8 @@

ASP.NET vNext - Help

| My Home - | My About - | My Help |

+ | My About + | My Help |

diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html index 27bc4cf95a..d10ba0b6f4 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html @@ -19,8 +19,8 @@

ASP.NET vNext - Home Page

| My Home - | My About - | My Help |

+ | My About + | My Help |

diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.ViewComponentTagHelpers.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.ViewComponentTagHelpers.html index b6caedd04c..c9bec6c144 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.ViewComponentTagHelpers.html +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.ViewComponentTagHelpers.html @@ -12,8 +12,8 @@

ASP.NET vNext -

| My Home - | My About - | My Help |

+ | My About + | My Help |

Items:
diff --git a/test/WebSites/ApiExplorerWebSite/ActionDescriptorChangeProvider.cs b/test/WebSites/ApiExplorerWebSite/ActionDescriptorChangeProvider.cs index abc1b3b8b3..266f99d3aa 100644 --- a/test/WebSites/ApiExplorerWebSite/ActionDescriptorChangeProvider.cs +++ b/test/WebSites/ApiExplorerWebSite/ActionDescriptorChangeProvider.cs @@ -9,20 +9,22 @@ namespace ApiExplorerWebSite { public class ActionDescriptorChangeProvider : IActionDescriptorChangeProvider { - private ActionDescriptorChangeProvider() + public ActionDescriptorChangeProvider(WellKnownChangeToken changeToken) { + ChangeToken = changeToken; } - public static ActionDescriptorChangeProvider Instance { get; } = new ActionDescriptorChangeProvider(); - - public CancellationTokenSource TokenSource { get; private set; } - - public bool HasChanged { get; set; } + public WellKnownChangeToken ChangeToken { get; } public IChangeToken GetChangeToken() { - TokenSource = new CancellationTokenSource(); - return new CancellationChangeToken(TokenSource.Token); + if (ChangeToken.TokenSource.IsCancellationRequested) + { + var changeTokenSource = new CancellationTokenSource(); + return new CancellationChangeToken(changeTokenSource.Token); + } + + return new CancellationChangeToken(ChangeToken.TokenSource.Token); } } } diff --git a/test/WebSites/ApiExplorerWebSite/ApiExplorerRouteChangeConvention.cs b/test/WebSites/ApiExplorerWebSite/ApiExplorerRouteChangeConvention.cs new file mode 100644 index 0000000000..c329b28c42 --- /dev/null +++ b/test/WebSites/ApiExplorerWebSite/ApiExplorerRouteChangeConvention.cs @@ -0,0 +1,35 @@ +// 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.Linq; +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace ApiExplorerWebSite +{ + public class ApiExplorerRouteChangeConvention : Attribute, IActionModelConvention + { + public ApiExplorerRouteChangeConvention(WellKnownChangeToken changeToken) + { + ChangeToken = changeToken; + } + + public WellKnownChangeToken ChangeToken { get; } + + public void Apply(ActionModel action) + { + if (action.Attributes.OfType().Any() && ChangeToken.TokenSource.IsCancellationRequested) + { + action.ActionName = "NewIndex"; + action.Selectors.Clear(); + action.Selectors.Add(new SelectorModel + { + AttributeRouteModel = new AttributeRouteModel + { + Template = "NewIndex" + } + }); + } + } + } +} diff --git a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerReloadableController.cs b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerReloadableController.cs index 31103ba0f2..d6372a8a86 100644 --- a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerReloadableController.cs +++ b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerReloadableController.cs @@ -1,45 +1,23 @@ // 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; -using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace ApiExplorerWebSite { [Route("ApiExplorerReload")] public class ApiExplorerReloadableController : Controller { - [ApiExplorerRouteChangeConvention] [Route("Index")] + [Reload] public string Index() => "Hello world"; [Route("Reload")] [PassThru] - public IActionResult Reload() + public IActionResult Reload([FromServices] WellKnownChangeToken changeToken) { - ActionDescriptorChangeProvider.Instance.HasChanged = true; - ActionDescriptorChangeProvider.Instance.TokenSource.Cancel(); + changeToken.TokenSource.Cancel(); return Ok(); } - - public class ApiExplorerRouteChangeConventionAttribute : Attribute, IActionModelConvention - { - public void Apply(ActionModel action) - { - if (ActionDescriptorChangeProvider.Instance.HasChanged) - { - action.ActionName = "NewIndex"; - action.Selectors.Clear(); - action.Selectors.Add(new SelectorModel - { - AttributeRouteModel = new AttributeRouteModel - { - Template = "NewIndex" - } - }); - } - } - } } } diff --git a/test/WebSites/ApiExplorerWebSite/ReloadAttribute.cs b/test/WebSites/ApiExplorerWebSite/ReloadAttribute.cs new file mode 100644 index 0000000000..9cb7222539 --- /dev/null +++ b/test/WebSites/ApiExplorerWebSite/ReloadAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace ApiExplorerWebSite +{ + public class ReloadAttribute : Attribute + { + } +} diff --git a/test/WebSites/ApiExplorerWebSite/Startup.cs b/test/WebSites/ApiExplorerWebSite/Startup.cs index e794f807ef..f5f96abfd5 100644 --- a/test/WebSites/ApiExplorerWebSite/Startup.cs +++ b/test/WebSites/ApiExplorerWebSite/Startup.cs @@ -6,6 +6,7 @@ using System.Linq; using ApiExplorerWebSite.Controllers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -19,6 +20,8 @@ namespace ApiExplorerWebSite public void ConfigureServices(IServiceCollection services) { services.AddTransient(); + + var wellKnownChangeToken = new WellKnownChangeToken(); services.AddMvc(options => { options.Filters.AddService(typeof(ApiExplorerDataFilter)); @@ -28,17 +31,19 @@ namespace ApiExplorerWebSite typeof(ApiExplorerVisbilityDisabledByConventionController))); options.Conventions.Add(new ApiExplorerInboundOutboundConvention( typeof(ApiExplorerInboundOutBoundController))); + options.Conventions.Add(new ApiExplorerRouteChangeConvention(wellKnownChangeToken)); var jsonOutputFormatter = options.OutputFormatters.OfType().First(); options.OutputFormatters.Clear(); options.OutputFormatters.Add(jsonOutputFormatter); options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); - }); + }) + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddSingleton(); - services.AddSingleton(ActionDescriptorChangeProvider.Instance); - services.AddSingleton(ActionDescriptorChangeProvider.Instance); + services.AddSingleton(); + services.AddSingleton(wellKnownChangeToken); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/ApiExplorerWebSite/WellKnownChangeToken.cs b/test/WebSites/ApiExplorerWebSite/WellKnownChangeToken.cs new file mode 100644 index 0000000000..a862f4f88f --- /dev/null +++ b/test/WebSites/ApiExplorerWebSite/WellKnownChangeToken.cs @@ -0,0 +1,12 @@ +// 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.Threading; + +namespace ApiExplorerWebSite +{ + public class WellKnownChangeToken + { + public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); + } +} diff --git a/test/WebSites/ApplicationModelWebSite/Startup.cs b/test/WebSites/ApplicationModelWebSite/Startup.cs index 37e97287fb..899b3c13fa 100644 --- a/test/WebSites/ApplicationModelWebSite/Startup.cs +++ b/test/WebSites/ApplicationModelWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace ApplicationModelWebSite @@ -20,7 +21,8 @@ namespace ApplicationModelWebSite options.Conventions.Add(new FromHeaderConvention()); options.Conventions.Add(new MultipleAreasControllerConvention()); options.Conventions.Add(new CloneActionConvention()); - }); + }) + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/BasicWebSite/StartupRequestLimitSize.cs b/test/WebSites/BasicWebSite/StartupRequestLimitSize.cs index 0338d0b217..066fc406da 100644 --- a/test/WebSites/BasicWebSite/StartupRequestLimitSize.cs +++ b/test/WebSites/BasicWebSite/StartupRequestLimitSize.cs @@ -5,6 +5,7 @@ using System; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace BasicWebSite @@ -13,7 +14,8 @@ namespace BasicWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.ConfigureBaseWebSiteAuthPolicies(); } diff --git a/test/WebSites/BasicWebSite/StartupWithCookieTempDataProviderAndCookieConsent.cs b/test/WebSites/BasicWebSite/StartupWithCookieTempDataProviderAndCookieConsent.cs index 0fdb7383b6..db15febae3 100644 --- a/test/WebSites/BasicWebSite/StartupWithCookieTempDataProviderAndCookieConsent.cs +++ b/test/WebSites/BasicWebSite/StartupWithCookieTempDataProviderAndCookieConsent.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace BasicWebSite @@ -10,7 +11,8 @@ namespace BasicWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.Configure(o => { diff --git a/test/WebSites/BasicWebSite/StartupWithSessionTempDataProvider.cs b/test/WebSites/BasicWebSite/StartupWithSessionTempDataProvider.cs index c05e83cb95..1d868bc638 100644 --- a/test/WebSites/BasicWebSite/StartupWithSessionTempDataProvider.cs +++ b/test/WebSites/BasicWebSite/StartupWithSessionTempDataProvider.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace BasicWebSite @@ -13,7 +14,8 @@ namespace BasicWebSite // CookieTempDataProvider is the default ITempDataProvider, so we must override it with session. services .AddMvc() - .AddSessionStateTempDataProvider(); + .AddSessionStateTempDataProvider() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddSession(); services.ConfigureBaseWebSiteAuthPolicies(); diff --git a/test/WebSites/ControllersFromServicesWebSite/Startup.cs b/test/WebSites/ControllersFromServicesWebSite/Startup.cs index 05653ebde6..b11534fedb 100644 --- a/test/WebSites/ControllersFromServicesWebSite/Startup.cs +++ b/test/WebSites/ControllersFromServicesWebSite/Startup.cs @@ -11,7 +11,7 @@ using ControllersFromServicesWebSite.Components; using ControllersFromServicesWebSite.TagHelpers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; @@ -36,7 +36,8 @@ namespace ControllersFromServicesWebSite }) .AddControllersAsServices() .AddViewComponentsAsServices() - .AddTagHelpersAsServices(); + .AddTagHelpersAsServices() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddTransient(); services.AddTransient(); diff --git a/test/WebSites/CorsWebSite/Startup.cs b/test/WebSites/CorsWebSite/Startup.cs index 5c8e115755..6ef084b83b 100644 --- a/test/WebSites/CorsWebSite/Startup.cs +++ b/test/WebSites/CorsWebSite/Startup.cs @@ -1,10 +1,9 @@ // 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.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace CorsWebSite @@ -13,7 +12,8 @@ namespace CorsWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.Configure(options => { options.AddPolicy( diff --git a/test/WebSites/CorsWebSite/StartupWithEndpointRouting.cs b/test/WebSites/CorsWebSite/StartupWith21Compat.cs similarity index 93% rename from test/WebSites/CorsWebSite/StartupWithEndpointRouting.cs rename to test/WebSites/CorsWebSite/StartupWith21Compat.cs index c620d62a58..b34b71f80c 100644 --- a/test/WebSites/CorsWebSite/StartupWithEndpointRouting.cs +++ b/test/WebSites/CorsWebSite/StartupWith21Compat.cs @@ -1,19 +1,19 @@ // 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.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace CorsWebSite { - public class StartupWithEndpointRouting + public class StartupWith21Compat { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(options => options.EnableEndpointRouting = true); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.Configure(options => { options.AddPolicy( diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs b/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs index 864fce79ea..3fc459a06f 100644 --- a/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs +++ b/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace ErrorPageMiddlewareWebSite @@ -13,7 +14,8 @@ namespace ErrorPageMiddlewareWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/FilesWebSite/Startup.cs b/test/WebSites/FilesWebSite/Startup.cs index aaa363af54..3e1404b3ff 100644 --- a/test/WebSites/FilesWebSite/Startup.cs +++ b/test/WebSites/FilesWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace FilesWebSite @@ -13,7 +14,8 @@ namespace FilesWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/HtmlGenerationWebSite/Startup.cs b/test/WebSites/HtmlGenerationWebSite/Startup.cs index a0c93159d6..a1b2a64b26 100644 --- a/test/WebSites/HtmlGenerationWebSite/Startup.cs +++ b/test/WebSites/HtmlGenerationWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.Extensions.DependencyInjection; @@ -16,7 +17,9 @@ namespace HtmlGenerationWebSite { // Add MVC services to the services container. Change default FormTagHelper.AntiForgery to false. Usually // null which is interpreted as true unless element includes an action attribute. - services.AddMvc().InitializeTagHelper((helper, _) => helper.Antiforgery = false); + services.AddMvc() + .InitializeTagHelper((helper, _) => helper.Antiforgery = false) + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddSingleton(typeof(ISignalTokenProviderService<>), typeof(SignalTokenProviderService<>)); services.AddSingleton(); diff --git a/test/WebSites/HtmlGenerationWebSite/StartupWith21CompatibilityBehavior.cs b/test/WebSites/HtmlGenerationWebSite/StartupWith21CompatibilityBehavior.cs new file mode 100644 index 0000000000..5c4fce1ed8 --- /dev/null +++ b/test/WebSites/HtmlGenerationWebSite/StartupWith21CompatibilityBehavior.cs @@ -0,0 +1,55 @@ +// 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.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.TagHelpers; +using Microsoft.Extensions.DependencyInjection; + +namespace HtmlGenerationWebSite +{ + public class StartupWith21CompatibilityBehavior + { + // Set up application services + public void ConfigureServices(IServiceCollection services) + { + // Add MVC services to the services container. Change default FormTagHelper.AntiForgery to false. Usually + // null which is interpreted as true unless element includes an action attribute. + services.AddMvc() + .InitializeTagHelper((helper, _) => helper.Antiforgery = false) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + + services.AddSingleton(typeof(ISignalTokenProviderService<>), typeof(SignalTokenProviderService<>)); + services.AddSingleton(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseStaticFiles(); + app.UseMvc(routes => + { + routes.MapRoute( + name: "areaRoute", + template: "{area:exists}/{controller}/{action}/{id?}", + defaults: new { action = "Index" }); + routes.MapRoute( + name: "productRoute", + template: "Product/{action}", + defaults: new { controller = "Product" }); + routes.MapRoute( + name: "default", + template: "{controller}/{action}/{id?}", + defaults: new { controller = "HtmlGeneration_Home", action = "Index" }); + }); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + new WebHostBuilder() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseStartup() + .UseKestrel() + .UseIISIntegration(); + } +} diff --git a/test/WebSites/HtmlGenerationWebSite/StartupWithCultureReplace.cs b/test/WebSites/HtmlGenerationWebSite/StartupWithCultureReplace.cs index ea197babb3..1214b96f88 100644 --- a/test/WebSites/HtmlGenerationWebSite/StartupWithCultureReplace.cs +++ b/test/WebSites/HtmlGenerationWebSite/StartupWithCultureReplace.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.DependencyInjection; namespace HtmlGenerationWebSite diff --git a/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Index.cshtml b/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Index.cshtml index c505df763e..24830788f5 100644 --- a/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Index.cshtml +++ b/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Index.cshtml @@ -19,7 +19,7 @@ Default Controller
Product Submit Fragment @@ -46,7 +46,7 @@
diff --git a/test/WebSites/RazorBuildWebSite/Startup.cs b/test/WebSites/RazorBuildWebSite/Startup.cs index b66bede92b..3d4858bd55 100644 --- a/test/WebSites/RazorBuildWebSite/Startup.cs +++ b/test/WebSites/RazorBuildWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace RazorBuildWebSite @@ -12,7 +13,8 @@ namespace RazorBuildWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/RazorPagesWebSite/Startup.cs b/test/WebSites/RazorPagesWebSite/Startup.cs index fe3cecc4e9..ea3a6bd601 100644 --- a/test/WebSites/RazorPagesWebSite/Startup.cs +++ b/test/WebSites/RazorPagesWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.Globalization; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using RazorPagesWebSite.Conventions; @@ -25,7 +26,8 @@ namespace RazorPagesWebSite options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty); options.Conventions.Add(new CustomModelTypeConvention()); }) - .WithRazorPagesAtContentRoot(); + .WithRazorPagesAtContentRoot() + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs index 4ee45cae12..0ded866ccf 100644 --- a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs +++ b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using RazorPagesWebSite.Conventions; @@ -24,7 +25,8 @@ namespace RazorPagesWebSite options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth"); options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous"); options.Conventions.Add(new CustomModelTypeConvention()); - }); + }) + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/RazorWebSite/Controllers/EmbeddedViewsController.cs b/test/WebSites/RazorWebSite/Controllers/EmbeddedViewsController.cs index ca653f83a0..d170e5cf57 100644 --- a/test/WebSites/RazorWebSite/Controllers/EmbeddedViewsController.cs +++ b/test/WebSites/RazorWebSite/Controllers/EmbeddedViewsController.cs @@ -7,6 +7,8 @@ namespace RazorWebSite.Controllers { public class EmbeddedViewsController : Controller { + public IActionResult Index() => null; + public IActionResult LookupByName() => View("Index"); public IActionResult LookupByPath() => View("/Views/EmbeddedViews/Index.cshtml"); diff --git a/test/WebSites/RazorWebSite/Startup.cs b/test/WebSites/RazorWebSite/Startup.cs index d0a4244558..c4b02e356f 100644 --- a/test/WebSites/RazorWebSite/Startup.cs +++ b/test/WebSites/RazorWebSite/Startup.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.DependencyInjection; @@ -41,7 +42,8 @@ namespace RazorWebSite options.HtmlHelperOptions.ValidationMessageElement = "validationMessageElement"; options.HtmlHelperOptions.ValidationSummaryMessageElement = "validationSummaryElement"; }) - .AddMvcLocalization(LanguageViewLocationExpanderFormat.SubFolder); + .AddMvcLocalization(LanguageViewLocationExpanderFormat.SubFolder) + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddTransient(); services.AddTransient(); diff --git a/test/WebSites/RazorWebSite/StartupDataAnnotations.cs b/test/WebSites/RazorWebSite/StartupDataAnnotations.cs index 7ec35e86fa..d3159ea501 100644 --- a/test/WebSites/RazorWebSite/StartupDataAnnotations.cs +++ b/test/WebSites/RazorWebSite/StartupDataAnnotations.cs @@ -6,7 +6,6 @@ using System.Globalization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; namespace RazorWebSite @@ -25,8 +24,8 @@ namespace RazorWebSite { options.DataAnnotationLocalizerProvider = (modelType, stringLocalizerFactory) => stringLocalizerFactory.Create(typeof(SingleType)); - }); - services.Configure(options => options.CompatibilityVersion = CompatibilityVersion.Latest); + }) + .SetCompatibilityVersion(CompatibilityVersion.Latest); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/test/WebSites/RoutingWebSite/Startup.cs b/test/WebSites/RoutingWebSite/Startup.cs index c7b905bffb..984e4cc5c2 100644 --- a/test/WebSites/RoutingWebSite/Startup.cs +++ b/test/WebSites/RoutingWebSite/Startup.cs @@ -1,9 +1,8 @@ // 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.IO; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -14,7 +13,8 @@ namespace RoutingWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddScoped(); services.AddSingleton(); @@ -29,8 +29,7 @@ namespace RoutingWebSite "adminRoute", "{area:exists}/{controller}/{action}", new { controller = "Home", action = "Index" }, - new { area = "Travel" } - ); + new { area = "Travel" }); routes.MapRoute( "ActionAsMethod", @@ -43,5 +42,4 @@ namespace RoutingWebSite }); } } -} - +} \ No newline at end of file diff --git a/test/WebSites/RoutingWebSite/StartupWithEndpointRouting.cs b/test/WebSites/RoutingWebSite/StartupWith21Compat.cs similarity index 90% rename from test/WebSites/RoutingWebSite/StartupWithEndpointRouting.cs rename to test/WebSites/RoutingWebSite/StartupWith21Compat.cs index 16bc62d81f..33c10a0275 100644 --- a/test/WebSites/RoutingWebSite/StartupWithEndpointRouting.cs +++ b/test/WebSites/RoutingWebSite/StartupWith21Compat.cs @@ -2,18 +2,19 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; namespace RoutingWebSite { - public class StartupWithEndpointRouting + public class StartupWith21Compat { // Set up application services public void ConfigureServices(IServiceCollection services) { services.AddMvc() - .AddMvcOptions(options => options.EnableEndpointRouting = true); + .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddScoped(); services.AddSingleton(); diff --git a/test/WebSites/SecurityWebSite/Startup.cs b/test/WebSites/SecurityWebSite/Startup.cs index 7dc96a5c2a..08db0bdd8d 100644 --- a/test/WebSites/SecurityWebSite/Startup.cs +++ b/test/WebSites/SecurityWebSite/Startup.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace SecurityWebSite @@ -14,7 +15,8 @@ namespace SecurityWebSite public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddAntiforgery(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => { diff --git a/test/WebSites/SecurityWebSite/StartupWith20CompatAndGlobalDenyAnonymousFilter.cs b/test/WebSites/SecurityWebSite/StartupWith20CompatAndGlobalDenyAnonymousFilter.cs new file mode 100644 index 0000000000..b674e76fe0 --- /dev/null +++ b/test/WebSites/SecurityWebSite/StartupWith20CompatAndGlobalDenyAnonymousFilter.cs @@ -0,0 +1,41 @@ +// 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 Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization.Policy; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace SecurityWebSite +{ + public class StartupWith20CompatAndGlobalDenyAnonymousFilter + { + public void ConfigureServices(IServiceCollection services) + { + services + .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.LoginPath = "/Home/Login"; + options.LogoutPath = "/Home/Logout"; + }).AddCookie("Cookie2"); + + services.AddMvc(o => + { + o.Filters.Add(new AuthorizeFilter()); + }) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_0); + + services.AddScoped(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseAuthentication(); + + app.UseMvcWithDefaultRoute(); + } + } +} diff --git a/test/WebSites/SecurityWebSite/StartupWithGlobalDenyAnonymousFilter.cs b/test/WebSites/SecurityWebSite/StartupWithGlobalDenyAnonymousFilter.cs index 4fa7b0e884..01c796512f 100644 --- a/test/WebSites/SecurityWebSite/StartupWithGlobalDenyAnonymousFilter.cs +++ b/test/WebSites/SecurityWebSite/StartupWithGlobalDenyAnonymousFilter.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.Extensions.DependencyInjection; @@ -24,7 +25,8 @@ namespace SecurityWebSite services.AddMvc(o => { o.Filters.Add(new AuthorizeFilter()); - }); + }) + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddScoped(); } diff --git a/test/WebSites/SimpleWebSite/Startup.cs b/test/WebSites/SimpleWebSite/Startup.cs index 50ad06a397..a4264f8abb 100644 --- a/test/WebSites/SimpleWebSite/Startup.cs +++ b/test/WebSites/SimpleWebSite/Startup.cs @@ -2,11 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; -using System.Text.Encodings.Web; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.WebEncoders.Testing; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; @@ -21,7 +20,8 @@ namespace SimpleWebSite .AddMvcCore() .AddAuthorization() .AddFormatterMappings(m => m.SetMediaTypeMappingForFormat("js", new MediaTypeHeaderValue("application/json"))) - .AddJsonFormatters(j => j.Formatting = Formatting.Indented); + .AddJsonFormatters(j => j.Formatting = Formatting.Indented) + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/TagHelpersWebSite/Startup.cs b/test/WebSites/TagHelpersWebSite/Startup.cs index f6c470b7fc..600b2b6c62 100644 --- a/test/WebSites/TagHelpersWebSite/Startup.cs +++ b/test/WebSites/TagHelpersWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace TagHelpersWebSite @@ -13,7 +14,8 @@ namespace TagHelpersWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/VersioningWebSite/Startup.cs b/test/WebSites/VersioningWebSite/Startup.cs index 5373436c06..7a94cb2360 100644 --- a/test/WebSites/VersioningWebSite/Startup.cs +++ b/test/WebSites/VersioningWebSite/Startup.cs @@ -1,9 +1,8 @@ // 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.IO; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -14,7 +13,8 @@ namespace VersioningWebSite public void ConfigureServices(IServiceCollection services) { // Add MVC services to the services container - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddScoped(); services.AddSingleton(); diff --git a/test/WebSites/VersioningWebSite/StartupWithEndpointRouting.cs b/test/WebSites/VersioningWebSite/StartupWith21Compat.cs similarity index 81% rename from test/WebSites/VersioningWebSite/StartupWithEndpointRouting.cs rename to test/WebSites/VersioningWebSite/StartupWith21Compat.cs index 9c72888db9..44bbc01aa5 100644 --- a/test/WebSites/VersioningWebSite/StartupWithEndpointRouting.cs +++ b/test/WebSites/VersioningWebSite/StartupWith21Compat.cs @@ -1,21 +1,20 @@ // 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.IO; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; namespace VersioningWebSite { - public class StartupWithEndpointRouting + public class StartupWith21Compat { public void ConfigureServices(IServiceCollection services) { // Add MVC services to the services container services.AddMvc() - .AddMvcOptions(options => options.EnableEndpointRouting = true); + .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddScoped(); services.AddSingleton(); diff --git a/test/WebSites/WebApiCompatShimWebSite/Startup.cs b/test/WebSites/WebApiCompatShimWebSite/Startup.cs index 3699ab9e60..b8dda25a9d 100644 --- a/test/WebSites/WebApiCompatShimWebSite/Startup.cs +++ b/test/WebSites/WebApiCompatShimWebSite/Startup.cs @@ -4,6 +4,7 @@ using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace WebApiCompatShimWebSite @@ -13,7 +14,10 @@ namespace WebApiCompatShimWebSite public void ConfigureServices(IServiceCollection services) { // Add MVC services to the services container - services.AddMvc().AddWebApiConventions(); + services.AddMvc() + .AddWebApiConventions() + .SetCompatibilityVersion(CompatibilityVersion.Latest) + .AddMvcOptions(options => options.EnableEndpointRouting = false); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/XmlFormattersWebSite/Startup.cs b/test/WebSites/XmlFormattersWebSite/Startup.cs index 31bce95f56..07bda1e37a 100644 --- a/test/WebSites/XmlFormattersWebSite/Startup.cs +++ b/test/WebSites/XmlFormattersWebSite/Startup.cs @@ -17,7 +17,8 @@ namespace XmlFormattersWebSite public void ConfigureServices(IServiceCollection services) { // Add MVC services to the services container - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); services.Configure(options => { From dfb579d45c7ba9f095b10505be57178897bf13ba Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 21 Aug 2018 17:01:20 -0700 Subject: [PATCH 2/3] [Fixes #8021] Copy the request headers before sending the request on the RedirectHandler If another handler modifies the request headers the modified headers get applied on subsequent requests, which is not correct. This change copies the headers before sending the request and uses the original headers for the redirect request instead of the potentially modified ones. --- .../Handlers/RedirectHandler.cs | 42 ++++++++++------- .../TestingInfrastructureTests.cs | 47 ++++++++++++++++++- .../Controllers/TestingController.cs | 26 ++++++++++ 3 files changed, 97 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Testing/Handlers/RedirectHandler.cs b/src/Microsoft.AspNetCore.Mvc.Testing/Handlers/RedirectHandler.cs index 63158713d3..71198d3d75 100644 --- a/src/Microsoft.AspNetCore.Mvc.Testing/Handlers/RedirectHandler.cs +++ b/src/Microsoft.AspNetCore.Mvc.Testing/Handlers/RedirectHandler.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -49,13 +50,14 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var remainingRedirects = MaxRedirects; - + var redirectRequest = new HttpRequestMessage(); var originalRequestContent = HasBody(request) ? await DuplicateRequestContent(request) : null; + CopyRequestHeaders(request.Headers, redirectRequest.Headers); var response = await base.SendAsync(request, cancellationToken); while (IsRedirect(response) && remainingRedirects >= 0) { remainingRedirects--; - var redirectRequest = GetRedirectRequest(response, originalRequestContent); + UpdateRedirectRequest(response, redirectRequest, originalRequestContent); originalRequestContent = HasBody(redirectRequest) ? await DuplicateRequestContent(redirectRequest) : null; response = await base.SendAsync(redirectRequest, cancellationToken); } @@ -95,6 +97,16 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers } } + private static void CopyRequestHeaders( + HttpRequestHeaders originalRequestHeaders, + HttpRequestHeaders newRequestHeaders) + { + foreach (var header in originalRequestHeaders) + { + newRequestHeaders.Add(header.Key, header.Value); + } + } + private static async Task<(Stream originalBody, Stream copy)> CopyBody(HttpRequestMessage request) { var originalBody = await request.Content.ReadAsStreamAsync(); @@ -116,8 +128,9 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers return (originalBody, bodyCopy); } - private static HttpRequestMessage GetRedirectRequest( + private static void UpdateRedirectRequest( HttpResponseMessage response, + HttpRequestMessage redirect, HttpContent originalContent) { var location = response.Headers.Location; @@ -128,34 +141,31 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers location); } - var redirect = !ShouldKeepVerb(response) ? - new HttpRequestMessage(HttpMethod.Get, location) : - new HttpRequestMessage(response.RequestMessage.Method, location) - { - Content = originalContent - }; - - foreach (var header in response.RequestMessage.Headers) + redirect.RequestUri = location; + if (!ShouldKeepVerb(response)) { - redirect.Headers.Add(header.Key, header.Value); + redirect.Method = HttpMethod.Get; + } + else + { + redirect.Method = response.RequestMessage.Method; + redirect.Content = originalContent; } foreach (var property in response.RequestMessage.Properties) { redirect.Properties.Add(property.Key, property.Value); } - - return redirect; } private static bool ShouldKeepVerb(HttpResponseMessage response) => response.StatusCode == HttpStatusCode.RedirectKeepVerb || (int)response.StatusCode == 308; - private bool IsRedirect(HttpResponseMessage response) => + private bool IsRedirect(HttpResponseMessage response) => response.StatusCode == HttpStatusCode.MovedPermanently || response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb || (int)response.StatusCode == 308; } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TestingInfrastructureTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TestingInfrastructureTests.cs index 9e5be478f6..afa57b5092 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TestingInfrastructureTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TestingInfrastructureTests.cs @@ -2,11 +2,13 @@ using System.Net; using System.Net.Http; using System.Net.Http.Formatting; +using System.Threading; using System.Threading.Tasks; using BasicWebSite; using BasicWebSite.Controllers; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.Mvc.Testing.Handlers; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -17,13 +19,14 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests { public TestingInfrastructureTests(WebApplicationFactory fixture) { - var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); - Client = factory.CreateClient(); + Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); + Client = Factory.CreateClient(); } private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => builder.ConfigureTestServices(s => s.AddSingleton()); + public WebApplicationFactory Factory { get; } public HttpClient Client { get; } [Fact] @@ -57,6 +60,22 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal(5, handlerResponse.Body); } + [Fact] + public async Task TestingInfrastructure_RedirectHandlerUsesOriginalRequestHeaders() + { + // Act + var request = new HttpRequestMessage(HttpMethod.Get, "Testing/RedirectHandler/Headers"); + var client = Factory.CreateDefaultClient( + new RedirectHandler(), new TestHandler()); + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var modifiedHeaderWasSent = await response.Content.ReadAsStringAsync(); + + Assert.Equal("false", modifiedHeaderWasSent); + } + [Fact] public async Task TestingInfrastructure_PostRedirectGetWorksWithCookies() { @@ -93,5 +112,29 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Message = "Test"; } } + + private class TestHandler : DelegatingHandler + { + public TestHandler() + { + } + + public TestHandler(HttpMessageHandler innerHandler) : base(innerHandler) + { + } + + public bool HeaderAdded { get; set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (!HeaderAdded) + { + request.Headers.Add("X-Added-Header", "true"); + HeaderAdded = true; + } + + return base.SendAsync(request, cancellationToken); + } + } } } diff --git a/test/WebSites/BasicWebSite/Controllers/TestingController.cs b/test/WebSites/BasicWebSite/Controllers/TestingController.cs index 07e76a3d6b..882ed935c9 100644 --- a/test/WebSites/BasicWebSite/Controllers/TestingController.cs +++ b/test/WebSites/BasicWebSite/Controllers/TestingController.cs @@ -37,6 +37,32 @@ namespace BasicWebSite.Controllers return Ok(new RedirectHandlerResponse { Url = value, Body = number.Value }); } + [HttpGet("Testing/RedirectHandler/Headers")] + public IActionResult RedirectHandlerHeaders() + { + if (!Request.Headers.TryGetValue("X-Added-Header", out var value)) + { + return Content("No header present"); + } + else + { + return RedirectToAction(nameof(RedirectHandlerHeadersRedirect)); + } + } + + [HttpGet("Testing/RedirectHandler/Headers/Redirect")] + public IActionResult RedirectHandlerHeadersRedirect() + { + if (Request.Headers.TryGetValue("X-Added-Header", out var value)) + { + return Content("true"); + } + else + { + return Content("false"); + } + } + [HttpGet("Testing/AntiforgerySimulator/{value}")] public IActionResult AntiforgerySimulator([FromRoute]int value) { From a7301120b1971d6d047c63db5e6911aa134f99a2 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 20 Aug 2018 18:51:16 -0700 Subject: [PATCH 3/3] Unwrap filter factories in TypeFilterAttribute & ServiceFilterAttribute Fixes #7855 --- .../ServiceFilterAttribute.cs | 12 +- .../TypeFilterAttribute.cs | 10 +- .../ServiceFilterAttributeTest.cs | 62 ++++++++++ .../TypeFilterAttributeTest.cs | 116 ++++++++++++++++++ 4 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Mvc.Core.Test/ServiceFilterAttributeTest.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.Core.Test/TypeFilterAttributeTest.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ServiceFilterAttribute.cs b/src/Microsoft.AspNetCore.Mvc.Core/ServiceFilterAttribute.cs index 525d1a82bb..9e16cb4715 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ServiceFilterAttribute.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ServiceFilterAttribute.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; @@ -58,14 +57,11 @@ namespace Microsoft.AspNetCore.Mvc throw new ArgumentNullException(nameof(serviceProvider)); } - var service = serviceProvider.GetRequiredService(ServiceType); - - var filter = service as IFilterMetadata; - if (filter == null) + var filter = (IFilterMetadata)serviceProvider.GetRequiredService(ServiceType); + if (filter is IFilterFactory filterFactory) { - throw new InvalidOperationException(Resources.FormatFilterFactoryAttribute_TypeMustImplementIFilter( - typeof(ServiceFilterAttribute).Name, - typeof(IFilterMetadata).Name)); + // Unwrap filter factories + filter = filterFactory.CreateInstance(serviceProvider); } return filter; diff --git a/src/Microsoft.AspNetCore.Mvc.Core/TypeFilterAttribute.cs b/src/Microsoft.AspNetCore.Mvc.Core/TypeFilterAttribute.cs index bc5c19802f..a2048512a6 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/TypeFilterAttribute.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/TypeFilterAttribute.cs @@ -73,11 +73,17 @@ namespace Microsoft.AspNetCore.Mvc if (_factory == null) { var argumentTypes = Arguments?.Select(a => a.GetType())?.ToArray(); - _factory = ActivatorUtilities.CreateFactory(ImplementationType, argumentTypes ?? Type.EmptyTypes); } - return (IFilterMetadata)_factory(serviceProvider, Arguments); + var filter = (IFilterMetadata)_factory(serviceProvider, Arguments); + if (filter is IFilterFactory filterFactory) + { + // Unwrap filter factories + filter = filterFactory.CreateInstance(serviceProvider); + } + + return filter; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ServiceFilterAttributeTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ServiceFilterAttributeTest.cs new file mode 100644 index 0000000000..e21e55018c --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ServiceFilterAttributeTest.cs @@ -0,0 +1,62 @@ +// 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.Filters; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc +{ + public class ServiceFilterAttributeTest + { + [Fact] + public void CreateService_GetsFilterFromServiceProvider() + { + // Arrange + var expected = new TestFilter(); + var serviceProvider = new ServiceCollection() + .AddSingleton(expected) + .BuildServiceProvider(); + + var serviceFilter = new ServiceFilterAttribute(typeof(TestFilter)); + + // Act + var filter = serviceFilter.CreateInstance(serviceProvider); + + // Assert + Assert.Same(expected, filter); + } + + [Fact] + public void CreateService_UnwrapsFilterFactory() + { + // Arrange + var serviceProvider = new ServiceCollection() + .AddSingleton(new TestFilterFactory()) + .BuildServiceProvider(); + + var serviceFilter = new ServiceFilterAttribute(typeof(TestFilterFactory)); + + // Act + var filter = serviceFilter.CreateInstance(serviceProvider); + + // Assert + Assert.IsType(filter); + } + + public class TestFilter : IFilterMetadata + { + } + + public class TestFilterFactory : IFilterFactory + { + public bool IsReusable => throw new NotImplementedException(); + + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return new TestFilter(); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/TypeFilterAttributeTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/TypeFilterAttributeTest.cs new file mode 100644 index 0000000000..27d407b837 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/TypeFilterAttributeTest.cs @@ -0,0 +1,116 @@ +// 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.Filters; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc +{ + public class TypeFilterAttributeTest + { + [Fact] + public void CreateService_TypeActivatesImplementationType() + { + // Arrange + var value = "Some value"; + var uri = new Uri("http://www.asp.net"); + var serviceProvider = new ServiceCollection() + .AddSingleton(value) + .AddSingleton(uri) + .BuildServiceProvider(); + + var typeFilter = new TypeFilterAttribute(typeof(TestFilter)); + + // Act + var filter = typeFilter.CreateInstance(serviceProvider); + + // Assert + var testFilter = Assert.IsType(filter); + Assert.Same(value, testFilter.Value); + Assert.Same(uri, testFilter.Uri); + } + + [Fact] + public void CreateService_UsesArguments() + { + // Arrange + var value = "Some value"; + var uri = new Uri("http://www.asp.net"); + var serviceProvider = new ServiceCollection() + .AddSingleton("Value in DI") + .AddSingleton(uri) + .BuildServiceProvider(); + + var typeFilter = new TypeFilterAttribute(typeof(TestFilter)) + { + Arguments = new[] { value, } + }; + + // Act + var filter = typeFilter.CreateInstance(serviceProvider); + + // Assert + var testFilter = Assert.IsType(filter); + Assert.Same(value, testFilter.Value); + Assert.Same(uri, testFilter.Uri); + } + + [Fact] + public void CreateService_UnwrapsFilterFactory() + { + // Arrange + var value = "Some value"; + var uri = new Uri("http://www.asp.net"); + var serviceProvider = new ServiceCollection() + .AddSingleton("Value in DI") + .AddSingleton(uri) + .BuildServiceProvider(); + + var typeFilter = new TypeFilterAttribute(typeof(TestFilterFactory)) + { + Arguments = new[] { value, } + }; + + // Act + var filter = typeFilter.CreateInstance(serviceProvider); + + // Assert + var testFilter = Assert.IsType(filter); + Assert.Same(value, testFilter.Value); + Assert.Same(uri, testFilter.Uri); + } + + public class TestFilter : IFilterMetadata + { + public TestFilter(string value, Uri uri) + { + Value = value; + Uri = uri; + } + + public string Value { get; } + public Uri Uri { get; } + } + + public class TestFilterFactory : IFilterFactory + { + private readonly string _value; + private readonly Uri _uri; + + public TestFilterFactory(string value, Uri uri) + { + _value = value; + _uri = uri; + } + + public bool IsReusable => throw new NotImplementedException(); + + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return new TestFilter(_value, _uri); + } + } + } +}