From c11fe23f31a66e2100e61a3418b8ef729babdb22 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 30 Oct 2019 15:21:13 -0700 Subject: [PATCH] Very candidacy before processing in PageLoaderMatcherPolicy (#16678) Fixes https://github.com/aspnet/AspNetCore/issues/13996 --- .../Infrastructure/PageLoaderMatcherPolicy.cs | 5 ++ .../Mvc.FunctionalTests/RoutingDynamicTest.cs | 41 +++++++++++- .../StartupForDynamicAndRazorPages.cs | 62 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicAndRazorPages.cs diff --git a/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageLoaderMatcherPolicy.cs b/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageLoaderMatcherPolicy.cs index 97951df719..3d2acb23e8 100644 --- a/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageLoaderMatcherPolicy.cs +++ b/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageLoaderMatcherPolicy.cs @@ -65,6 +65,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure for (var i = 0; i < candidates.Count; i++) { + if (!candidates.IsValidCandidate(i)) + { + continue; + } + ref var candidate = ref candidates[i]; var endpoint = candidate.Endpoint; diff --git a/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicTest.cs b/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicTest.cs index 0974a23848..bd306b7bbf 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicTest.cs +++ b/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicTest.cs @@ -6,6 +6,8 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using RoutingWebSite; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -14,12 +16,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests { public RoutingDynamicTest(MvcTestFixture fixture) { - var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); - Client = factory.CreateDefaultClient(); + Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); + Client = Factory.CreateDefaultClient(); } private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => builder.UseStartup(); + public WebApplicationFactory Factory { get; } public HttpClient Client { get; } [Fact] @@ -99,5 +102,39 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Hello from dynamic page: /DynamicPage", content); } + + [Fact] + public async Task AppWithDynamicRouteAndMapRazorPages_CanRouteToRazorPage() + { + // Regression test for https://github.com/aspnet/AspNetCore/issues/13996 + // Arrange + var client = Factory.WithWebHostBuilder(b => b.UseStartup()).CreateDefaultClient(); + var url = "/PageWithLinks"; + + // Act + var response = await client.GetAsync(url); + + // Assert + var document = await response.GetHtmlDocumentAsync(); + var editLink = document.RequiredQuerySelector("#editlink"); + Assert.Equal("/Edit/10", editLink.GetAttribute("href")); + } + + [Fact] + public async Task AppWithDynamicRouteAndMapRazorPages_CanRouteToDynamicController() + { + // Regression test for https://github.com/aspnet/AspNetCore/issues/13996 + // Arrange + var client = Factory.WithWebHostBuilder(b => b.UseStartup()).CreateDefaultClient(); + var url = "/de/area%3Dadmin,controller%3Ddynamic,action%3Dindex"; + + // Act + var response = await client.GetAsync(url); + + // Assert + await response.AssertStatusCodeAsync(HttpStatusCode.OK); + var content = await response.Content.ReadAsStringAsync(); + Assert.StartsWith("Hello from dynamic controller", content); + } } } diff --git a/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicAndRazorPages.cs b/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicAndRazorPages.cs new file mode 100644 index 0000000000..7aa4e9f609 --- /dev/null +++ b/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicAndRazorPages.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.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace RoutingWebSite +{ + // For by tests for a mix of dynamic routing + Razor Pages + public class StartupForDynamicAndRazorPages + { + public void ConfigureServices(IServiceCollection services) + { + services + .AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Latest); + + services.AddSingleton(); + + // Used by some controllers defined in this project. + services.Configure(options => options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer)); + } + + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + endpoints.MapDynamicControllerRoute("{language}/{**slug}"); + }); + } + + private class Transformer : DynamicRouteValueTransformer + { + // Turns a format like `controller=Home,action=Index` into an RVD + public override ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + if (!(values["slug"] is string slug)) + { + return new ValueTask(values); + } + + var kvps = slug.Split(","); + + var results = new RouteValueDictionary(); + foreach (var kvp in kvps) + { + var split = kvp.Split("="); + results[split[0]] = split[1]; + } + + return new ValueTask(results); + } + } + } +}