React to routing cleanup

This commit is contained in:
Ryan Nowak 2015-12-07 10:37:10 -08:00
parent 99f501152b
commit 09a293afe9
16 changed files with 4 additions and 263 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.24627.0
VisualStudioVersion = 14.0.24711.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -114,8 +114,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UserClassLibrary", "test\We
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ControllerDiscoveryConventionsWebSite", "test\WebSites\ControllerDiscoveryConventionsWebSite\ControllerDiscoveryConventionsWebSite.xproj", "{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BestEffortLinkGenerationWebSite", "test\WebSites\BestEffortLinkGenerationWebSite\BestEffortLinkGenerationWebSite.xproj", "{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LowercaseUrlsWebSite", "test\WebSites\LowercaseUrlsWebSite\LowercaseUrlsWebSite.xproj", "{BCDB13A6-7D6E-485E-8424-A156432B71AC}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TestCommon", "test\Microsoft.AspNet.Mvc.TestCommon\Microsoft.AspNet.Mvc.TestCommon.xproj", "{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}"
@ -702,18 +700,6 @@ Global
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|x86.ActiveCfg = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|x86.Build.0 = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|x86.ActiveCfg = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Debug|x86.Build.0 = Debug|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|Any CPU.Build.0 = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|x86.ActiveCfg = Release|Any CPU
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34}.Release|x86.Build.0 = Release|Any CPU
{BCDB13A6-7D6E-485E-8424-A156432B71AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCDB13A6-7D6E-485E-8424-A156432B71AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCDB13A6-7D6E-485E-8424-A156432B71AC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -1092,7 +1078,6 @@ Global
{551DC89E-2A13-4CF2-83D7-1ADD802443D5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{B11C99C9-E577-4CA2-AC53-4F20EA71AD34} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{BCDB13A6-7D6E-485E-8424-A156432B71AC} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}

View File

@ -18,18 +18,15 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
{
public class DefaultActionSelector : IActionSelector
{
private readonly IActionDescriptorsCollectionProvider _actionDescriptorsCollectionProvider;
private readonly IActionSelectorDecisionTreeProvider _decisionTreeProvider;
private readonly IActionConstraintProvider[] _actionConstraintProviders;
private ILogger _logger;
public DefaultActionSelector(
IActionDescriptorsCollectionProvider actionDescriptorsCollectionProvider,
IActionSelectorDecisionTreeProvider decisionTreeProvider,
IEnumerable<IActionConstraintProvider> actionConstraintProviders,
ILoggerFactory loggerFactory)
{
_actionDescriptorsCollectionProvider = actionDescriptorsCollectionProvider;
_decisionTreeProvider = decisionTreeProvider;
_actionConstraintProviders = actionConstraintProviders.OrderBy(item => item.Order).ToArray();
_logger = loggerFactory.CreateLogger<DefaultActionSelector>();
@ -211,30 +208,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
}
}
// This method attempts to ensure that the route that's about to generate a link will generate a link
// to an existing action. This method is called by a route (through MvcApplication) prior to generating
// any link - this gives WebFX a chance to 'veto' the values provided by a route.
//
// This method does not take httpmethod or dynamic action constraints into account.
public virtual bool HasValidAction(VirtualPathContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.ProvidedValues == null)
{
// We need the route's values to be able to double check our work.
return false;
}
var tree = _decisionTreeProvider.DecisionTree;
var matchingRouteConstraints = tree.Select(context.ProvidedValues);
return matchingRouteConstraints.Count > 0;
}
private IReadOnlyList<IActionConstraint> GetConstraints(HttpContext httpContext, ActionDescriptor action)
{
if (action.ActionConstraints == null || action.ActionConstraints.Count == 0)

View File

@ -10,7 +10,5 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
public interface IActionSelector
{
Task<ActionDescriptor> SelectAsync(RouteContext context);
bool HasValidAction(VirtualPathContext context);
}
}

View File

@ -34,8 +34,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
throw new ArgumentNullException(nameof(context));
}
context.IsBound = true;
// We return null here because we're not responsible for generating the url, the route is.
return null;
}

View File

@ -55,40 +55,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
Assert.Equal(expectedMessage, sink.Writes[0].State?.ToString());
}
[Fact]
public void HasValidAction_Match()
{
// Arrange
var actions = GetActions();
var selector = CreateSelector(actions);
var context = CreateContext(new { });
context.ProvidedValues = new RouteValueDictionary(new { controller = "Home", action = "Index" });
// Act
var isValid = selector.HasValidAction(context);
// Assert
Assert.True(isValid);
}
[Fact]
public void HasValidAction_NoMatch()
{
// Arrange
var actions = GetActions();
var selector = CreateSelector(actions);
var context = CreateContext(new { });
context.ProvidedValues = new RouteValueDictionary(new { controller = "Home", action = "FakeAction" });
// Act
var isValid = selector.HasValidAction(context);
// Assert
Assert.False(isValid);
}
[Fact]
public async Task SelectAsync_PrefersActionWithConstraints()
{
@ -652,7 +618,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
};
var defaultActionSelector = new DefaultActionSelector(
actionDescriptorsCollectionProvider,
decisionTreeProvider,
actionConstraintProviders,
NullLoggerFactory.Instance);
@ -737,7 +702,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
};
return new DefaultActionSelector(
actionProvider.Object,
decisionTreeProvider,
actionConstraintProviders,
loggerFactory);

View File

@ -991,7 +991,6 @@ namespace Microsoft.AspNet.Mvc.Routing
var target = new Mock<IRouter>(MockBehavior.Strict);
target
.Setup(router => router.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(context => context.IsBound = true)
.Returns<VirtualPathContext>(context => null);
routeBuilder.DefaultHandler = target.Object;
@ -1006,7 +1005,6 @@ namespace Microsoft.AspNet.Mvc.Routing
var mockHttpRoute = new Mock<IRouter>();
mockHttpRoute
.Setup(mock => mock.GetVirtualPath(It.Is<VirtualPathContext>(c => string.Equals(c.RouteName, mockRouteName))))
.Callback<VirtualPathContext>(c => c.IsBound = true)
.Returns(new VirtualPathData(mockHttpRoute.Object, mockTemplateValue));
routeBuilder.Routes.Add(mockHttpRoute.Object);
@ -1017,7 +1015,6 @@ namespace Microsoft.AspNet.Mvc.Routing
{
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
context.IsBound = true;
return null;
}

View File

@ -1,42 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class BestEffortLinkGenerationTest : IClassFixture<MvcTestFixture<BestEffortLinkGenerationWebSite.Startup>>
{
private const string ExpectedOutput = @"<html>
<body>
<a href=""/Home/About"">About Us</a>
</body>
</html>";
public BestEffortLinkGenerationTest(MvcTestFixture<BestEffortLinkGenerationWebSite.Startup> fixture)
{
Client = fixture.Client;
}
public HttpClient Client { get; }
[Fact]
public async Task GenerateLink_NonExistentAction()
{
// Arrange
var url = "http://localhost/Home/Index";
// Act
var response = await Client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(ExpectedOutput, content, ignoreLineEndingDifferences: true);
}
}
}

View File

@ -14,7 +14,6 @@
"ApiExplorerWebSite": "1.0.0",
"ApplicationModelWebSite": "1.0.0",
"BasicWebSite": "1.0.0",
"BestEffortLinkGenerationWebSite": "1.0.0",
"CompositeViewEngineWebSite": "1.0.0",
"ContentNegotiationWebSite": "1.0.0",
"ControllerDiscoveryConventionsWebSite": "1.0.0",

View File

@ -194,18 +194,6 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("No URL for remote validation could be found.", exception.Message);
}
[Fact]
public void GetClientValidationRules_WithActionController_NoController_Throws()
{
// Arrange
var attribute = new RemoteAttribute("Action", "Controller");
var context = GetValidationContextWithNoController();
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => attribute.GetClientValidationRules(context));
Assert.Equal("No URL for remote validation could be found.", exception.Message);
}
[Fact]
public void GetClientValidationRules_WithRoute_CallsUrlHelperWithExpectedValues()
{
@ -505,35 +493,9 @@ namespace Microsoft.AspNet.Mvc
return new ClientModelValidationContext(actionContext, _metadata, _metadataProvider);
}
private static ClientModelValidationContext GetValidationContextWithNoController()
{
var serviceCollection = GetServiceCollection();
var serviceProvider = serviceCollection.BuildServiceProvider();
var routeCollection = GetRouteCollectionWithNoController(serviceProvider);
var routeData = new RouteData
{
Routers =
{
routeCollection,
},
};
var context = GetActionContext(serviceProvider, routeData);
var urlHelper = new UrlHelper(context);
var factory = new Mock<IUrlHelperFactory>();
factory
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
.Returns(urlHelper);
serviceCollection.AddSingleton<IUrlHelperFactory>(factory.Object);
context.HttpContext.RequestServices = serviceCollection.BuildServiceProvider();
return new ClientModelValidationContext(context, _metadata, _metadataProvider);
}
private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
{
var builder = GetRouteBuilder(serviceProvider, isBound: true);
var builder = GetRouteBuilder(serviceProvider);
// Setting IsBound to true makes order more important than usual. First try the route that requires the
// area value. Skip usual "area:exists" constraint because that isn't relevant for link generation and it
@ -546,13 +508,13 @@ namespace Microsoft.AspNet.Mvc
private static IRouter GetRouteCollectionWithNoController(IServiceProvider serviceProvider)
{
var builder = GetRouteBuilder(serviceProvider, isBound: false);
var builder = GetRouteBuilder(serviceProvider);
builder.MapRoute("default", "static/route");
return builder.Build();
}
private static RouteBuilder GetRouteBuilder(IServiceProvider serviceProvider, bool isBound)
private static RouteBuilder GetRouteBuilder(IServiceProvider serviceProvider)
{
var builder = new RouteBuilder
{
@ -562,7 +524,6 @@ namespace Microsoft.AspNet.Mvc
var handler = new Mock<IRouter>(MockBehavior.Strict);
handler
.Setup(router => router.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(context => context.IsBound = isBound)
.Returns((VirtualPathData)null);
builder.DefaultHandler = handler.Object;

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" 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)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>b11c99c9-e577-4ca2-ac53-4f20ea71ad34</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>11623</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -1,15 +0,0 @@
// 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.AspNet.Mvc;
namespace BestEffortLinkGenerationWebSite
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
}

View File

@ -1,30 +0,0 @@
// 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.AspNet.Builder;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
namespace BestEffortLinkGenerationWebSite
{
public class Startup
{
// Set up application services
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<RouteOptions>((options) =>
{
options.UseBestEffortLinkGeneration = true;
});
}
public void Configure(IApplicationBuilder app)
{
app.UseCultureReplacer();
app.UseMvcWithDefaultRoute();
}
}
}

View File

@ -1,5 +0,0 @@
<html>
<body>
@Html.ActionLink("About Us", "About")
</body>
</html>

View File

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

View File

@ -1,5 +0,0 @@
BestEffortLinkGenerationWebSite
===
This web site illustrates how to use best-effort link generation, which will allow conventional
routes to generate a link to an action which doesn't exist.