This additional constraint enables adding a route to the template (and potentially to the UseMvc method) without actually implementing the actual artifact.

For example without adding an area to a controller, a route can still be added to the template.
- Also added functional tests.
This commit is contained in:
harshgMSFT 2014-06-06 14:37:29 -07:00
parent b58083f73a
commit 10285d7d39
27 changed files with 741 additions and 50 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.21708.0
VisualStudioVersion = 14.0.21806.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -17,7 +17,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Core",
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.ModelBinding", "src\Microsoft.AspNet.Mvc.ModelBinding\Microsoft.AspNet.Mvc.ModelBinding.kproj", "{FA915D3D-22C3-4478-97F2-A81D28B6C503}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Common", "src\Microsoft.AspNet.Mvc.Common\Microsoft.AspNet.Mvc.Common.kproj", "{F3DF6D0B-16FE-4402-B92C-7243A75CF1FD}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Common", "src\Microsoft.AspNet.Mvc.Common\Microsoft.AspNet.Mvc.Common.kproj", "{F3DF6D0B-16FE-4402-B92C-7243A75CF1FD}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.ModelBinding.Test", "test\Microsoft.AspNet.Mvc.ModelBinding.Test\Microsoft.AspNet.Mvc.ModelBinding.Test.kproj", "{3B8DC0C0-6C55-4034-AD96-DE1000928E6B}"
EndProject
@ -37,6 +37,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Functi
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BasicWebSite", "test\WebSites\BasicWebSite\BasicWebSite.kproj", "{34DF1487-12C6-476C-BE0A-F31DF1939AE5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "InlineConstraintsWebSite", "test\WebSites\InlineConstraintsWebSite\InlineConstraintsWebSite.kproj", "{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -177,6 +179,16 @@ Global
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|x86.ActiveCfg = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|x86.ActiveCfg = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Release|Any CPU.Build.0 = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -196,5 +208,6 @@ Global
{16703B76-C9F7-4C75-AE6C-53D92E308E3C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{323D0C04-B518-4A8F-8A8E-3546AD153D34} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{34DF1487-12C6-476C-BE0A-F31DF1939AE5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -1,6 +1,7 @@
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using MvcSample.Web.Filters;
using MvcSample.Web.Services;
@ -63,7 +64,7 @@ namespace MvcSample.Web
app.UseMvc(routes =>
{
routes.MapRoute("areaRoute", "{area}/{controller}/{action}");
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}");
routes.MapRoute(
"controllerActionRoute",

View File

@ -345,19 +345,12 @@ namespace Microsoft.AspNet.Mvc
if (descriptors == null)
{
throw new InvalidOperationException(
Resources.FormatPropertyOfTypeCannotBeNull(_actionDescriptorsCollectionProvider.GetType(),
"ActionDescriptors"));
Resources.FormatPropertyOfTypeCannotBeNull("ActionDescriptors",
_actionDescriptorsCollectionProvider.GetType()
));
}
var items = descriptors.Items;
if (items == null)
{
throw new InvalidOperationException(
Resources.FormatPropertyOfTypeCannotBeNull(descriptors.GetType(), "Items"));
}
return items;
return descriptors.Items;
}
private class ActionDescriptorCandidate

View File

@ -0,0 +1,101 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
public class KnownRouteValueConstraint : IRouteConstraint
{
private RouteValuesCollection _cachedValuesCollection;
public bool Match([NotNull] HttpContext httpContext,
[NotNull] IRouter route,
[NotNull] string routeKey,
[NotNull] IDictionary<string, object> values,
RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(routeKey, out value))
{
string valueAsString = value as string;
if (valueAsString != null)
{
var allValues = GetAndCacheAllMatchingValues(routeKey, httpContext);
var match = allValues.Any(existingRouteValue =>
existingRouteValue.Equals(
valueAsString,
StringComparison.OrdinalIgnoreCase));
return match;
}
}
return false;
}
private string[] GetAndCacheAllMatchingValues(string routeKey, HttpContext httpContext)
{
var actionDescriptors = GetAndValidateActionDescriptorsCollection(httpContext);
var version = actionDescriptors.Version;
var valuesCollection = _cachedValuesCollection;
if (valuesCollection == null ||
version != valuesCollection.Version)
{
var routeValueCollection = actionDescriptors
.Items
.Select(ad => ad.RouteConstraints
.FirstOrDefault(
c => c.RouteKey == routeKey &&
c.KeyHandling == RouteKeyHandling.RequireKey))
.Where(rc => rc != null)
.Select(rc => rc.RouteValue)
.Distinct()
.ToArray();
valuesCollection = new RouteValuesCollection(version, routeValueCollection);
_cachedValuesCollection = valuesCollection;
}
return _cachedValuesCollection.Items;
}
private static ActionDescriptorsCollection GetAndValidateActionDescriptorsCollection(HttpContext httpContext)
{
var provider = httpContext.ApplicationServices
.GetService<IActionDescriptorsCollectionProvider>();
var descriptors = provider.ActionDescriptors;
if (descriptors == null)
{
throw new InvalidOperationException(
Resources.FormatPropertyOfTypeCannotBeNull("ActionDescriptors",
provider.GetType()
));
}
return descriptors;
}
private class RouteValuesCollection
{
public RouteValuesCollection(int version, string[] items)
{
Version = version;
Items = items;
}
public int Version { get; private set; }
public string[] Items { get; private set; }
}
}
}

View File

@ -202,6 +202,7 @@
<Compile Include="Rendering\ViewEngineResult.cs" />
<Compile Include="RouteConstraintAttribute.cs" />
<Compile Include="RouteDataActionConstraint.cs" />
<Compile Include="KnownRouteValueConstraint.cs" />
<Compile Include="RouteKeyHandling.cs" />
<Compile Include="TemplateInfo.cs" />
<Compile Include="UrlHelper.cs" />

View File

@ -13,13 +13,23 @@ namespace Microsoft.Framework.DependencyInjection
public static IServiceCollection AddMvc(this IServiceCollection services)
{
services.Add(RoutingServices.GetDefaultServices());
AddMvcRouteOptions(services);
return services.Add(MvcServices.GetDefaultServices());
}
public static IServiceCollection AddMvc(this IServiceCollection services, IConfiguration configuration)
{
services.Add(RoutingServices.GetDefaultServices());
AddMvcRouteOptions(services);
return services.Add(MvcServices.GetDefaultServices(configuration));
}
private static void AddMvcRouteOptions(IServiceCollection services)
{
services.SetupOptions<RouteOptions>(routeOptions =>
routeOptions.ConstraintMap
.Add("exists",
typeof(KnownRouteValueConstraint)));
}
}
}

View File

@ -0,0 +1,198 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if NET45
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Routing.Tests
{
public class KnownRouteValueConstraintTests
{
private readonly IRouteConstraint _constraint = new KnownRouteValueConstraint();
[Theory]
[InlineData("area", RouteDirection.IncomingRequest)]
[InlineData("controller", RouteDirection.IncomingRequest)]
[InlineData("action", RouteDirection.IncomingRequest)]
[InlineData("randomKey", RouteDirection.IncomingRequest)]
[InlineData("area", RouteDirection.UrlGeneration)]
[InlineData("controller", RouteDirection.UrlGeneration)]
[InlineData("action", RouteDirection.UrlGeneration)]
[InlineData("randomKey", RouteDirection.UrlGeneration)]
public void RouteKey_DoesNotExist_MatchFails(string keyName, RouteDirection direction)
{
// Arrange
var values = new Dictionary<string, object>();
var httpContext = GetHttpContext(new ActionDescriptor());
var route = (new Mock<IRouter>()).Object;
// Act
var match = _constraint.Match(httpContext, route, keyName, values, direction);
// Assert
Assert.False(match);
}
[Theory]
[InlineData("area", RouteDirection.IncomingRequest)]
[InlineData("controller", RouteDirection.IncomingRequest)]
[InlineData("action", RouteDirection.IncomingRequest)]
[InlineData("randomKey", RouteDirection.IncomingRequest)]
[InlineData("area", RouteDirection.UrlGeneration)]
[InlineData("controller", RouteDirection.UrlGeneration)]
[InlineData("action", RouteDirection.UrlGeneration)]
[InlineData("randomKey", RouteDirection.UrlGeneration)]
public void RouteKey_Exists_MatchSucceeds(string keyName, RouteDirection direction)
{
// Arrange
var actionDescriptor = CreateActionDescriptor("testArea",
"testController",
"testAction");
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint("randomKey", "testRandom"));
var httpContext = GetHttpContext(actionDescriptor);
var route = (new Mock<IRouter>()).Object;
var values = new Dictionary<string, object>()
{
{ "area", "testArea" },
{ "controller", "testController" },
{ "action", "testAction" },
{ "randomKey", "testRandom" }
};
// Act
var match = _constraint.Match(httpContext, route, keyName, values, direction);
// Assert
Assert.True(match);
}
[Theory]
[InlineData("area", RouteDirection.IncomingRequest)]
[InlineData("controller", RouteDirection.IncomingRequest)]
[InlineData("action", RouteDirection.IncomingRequest)]
[InlineData("randomKey", RouteDirection.IncomingRequest)]
[InlineData("area", RouteDirection.UrlGeneration)]
[InlineData("controller", RouteDirection.UrlGeneration)]
[InlineData("action", RouteDirection.UrlGeneration)]
[InlineData("randomKey", RouteDirection.UrlGeneration)]
public void RouteValue_DoesNotExists_MatchFails(string keyName, RouteDirection direction)
{
// Arrange
var actionDescriptor = CreateActionDescriptor("testArea",
"testController",
"testAction");
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint("randomKey", "testRandom"));
var httpContext = GetHttpContext(actionDescriptor);
var route = (new Mock<IRouter>()).Object;
var values = new Dictionary<string, object>()
{
{ "area", "invalidTestArea" },
{ "controller", "invalidTestController" },
{ "action", "invalidTestAction" },
{ "randomKey", "invalidTestRandom" }
};
// Act
var match = _constraint.Match(httpContext, route, keyName, values, direction);
// Assert
Assert.False(match);
}
[Theory]
[InlineData(RouteDirection.IncomingRequest)]
[InlineData(RouteDirection.UrlGeneration)]
public void RouteValue_IsNotAString_MatchFails(RouteDirection direction)
{
var actionDescriptor = CreateActionDescriptor("testArea",
controller: null,
action: null);
var httpContext = GetHttpContext(actionDescriptor);
var route = (new Mock<IRouter>()).Object;
var values = new Dictionary<string, object>()
{
{ "area", 12 },
};
// Act
var match = _constraint.Match(httpContext, route, "area", values, direction);
// Assert
Assert.False(match);
}
[Theory]
[InlineData(RouteDirection.IncomingRequest)]
[InlineData(RouteDirection.UrlGeneration)]
public void ActionDescriptorsCollection_SettingNullValue_Throws(RouteDirection direction)
{
// Arrange
var httpContext = new Mock<HttpContext>();
httpContext.Setup(o => o.ApplicationServices
.GetService(typeof(IActionDescriptorsCollectionProvider)))
.Returns(new Mock<IActionDescriptorsCollectionProvider>().Object);
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(
() => _constraint.Match(httpContext.Object,
null,
"area",
new Dictionary<string, object>{ { "area", "area" } },
direction));
Assert.Equal("The 'ActionDescriptors' property of "+
"'Castle.Proxies.IActionDescriptorsCollectionProviderProxy' must not be null.",
ex.Message);
}
private static HttpContext GetHttpContext(ActionDescriptor actionDescriptor)
{
var actionProvider = new Mock<INestedProviderManager<ActionDescriptorProviderContext>>(
MockBehavior.Strict);
actionProvider
.Setup(p => p.Invoke(It.IsAny<ActionDescriptorProviderContext>()))
.Callback<ActionDescriptorProviderContext>(c => c.Results.Add(actionDescriptor));
var context = new Mock<HttpContext>();
context.Setup(o => o.ApplicationServices
.GetService(typeof(INestedProviderManager<ActionDescriptorProviderContext>)))
.Returns(actionProvider.Object);
context.Setup(o => o.ApplicationServices
.GetService(typeof(IActionDescriptorsCollectionProvider)))
.Returns(new DefaultActionDescriptorsCollectionProvider(context.Object.ApplicationServices));
return context.Object;
}
private static ActionDescriptor CreateActionDescriptor(string area, string controller, string action)
{
var actionDescriptor = new ActionDescriptor()
{
Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action),
RouteConstraints = new List<RouteDataActionConstraint>(),
};
actionDescriptor.RouteConstraints.Add(
area == null ?
new RouteDataActionConstraint("area", RouteKeyHandling.DenyKey) :
new RouteDataActionConstraint("area", area));
actionDescriptor.RouteConstraints.Add(
controller == null ?
new RouteDataActionConstraint("controller", RouteKeyHandling.DenyKey) :
new RouteDataActionConstraint("controller", controller));
actionDescriptor.RouteConstraints.Add(
action == null ?
new RouteDataActionConstraint("action", RouteKeyHandling.DenyKey) :
new RouteDataActionConstraint("action", action));
return actionDescriptor;
}
}
}
#endif

View File

@ -55,6 +55,7 @@
<Compile Include="Rendering\HtmlAttributePropertyHelperTest.cs" />
<Compile Include="Rendering\ViewContextTests.cs" />
<Compile Include="Rendering\ViewDataOfTTest.cs" />
<Compile Include="KnownRouteValueConstraintTests.cs" />
<Compile Include="TestController.cs" />
<Compile Include="TypeHelperTest.cs" />
<Compile Include="UrlHelperTest.cs" />

View File

@ -2,16 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using BasicWebSite;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Infrastructure;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
@ -29,24 +24,15 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
public BasicTests()
{
var originalProvider = CallContextServiceLocator.Locator.ServiceProvider;
IApplicationEnvironment appEnvironment = originalProvider.GetService<IApplicationEnvironment>();
// When an application executes in a regular context, the application base path points to the root
// directory where the application is located, for example MvcSample.Web. However, when executing
// an aplication as part of a test, the ApplicationBasePath of the IApplicationEnvironment points
// to the root folder of the test project.
// To compensate for this, we need to calculate the original path and override the application
// environment value so that components like the view engine work properly in the context of the
// test.
string appBasePath = CalculateApplicationBasePath(appEnvironment);
_provider = new ServiceCollection()
.AddInstance(typeof(IApplicationEnvironment), new TestApplicationEnvironment(appEnvironment, appBasePath))
.BuildServiceProvider(originalProvider);
_provider = TestHelper.CreateServices("BasicWebSite");
}
[Fact]
public async Task CanRender_ViewsWithLayout()
[InlineData("http://localhost/")]
[InlineData("http://localhost/Home")]
[InlineData("http://localhost/Home/Index")]
[InlineData("http://localhost/Users")]
[InlineData("http://localhost/Monitor/CountActionDescriptorInvocations")]
public async Task CanRender_ViewsWithLayout(string url)
{
// Arrange
var server = TestServer.Create(_provider, _app);
@ -59,7 +45,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Act
// The host is not important as everything runs in memory and tests are isolated from each other.
var result = await client.GetAsync("http://localhost/");
var result = await client.GetAsync(url);
var responseContent = await result.ReadBodyAsStringAsync();
// Assert
@ -129,18 +115,5 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expectedContent, results[1]);
Assert.Equal(expectedContent, results[2]);
}
// Calculate the path relative to the current application base path.
private static string CalculateApplicationBasePath(IApplicationEnvironment appEnvironment)
{
// Mvc/test/Microsoft.AspNet.Mvc.FunctionalTests
var appBase = appEnvironment.ApplicationBasePath;
// Mvc/test
var test = Path.GetDirectoryName(appBase);
// Mvc/test/WebSites/BasicWebSite
return Path.GetFullPath(Path.Combine(appBase, "..", "WebSites", "BasicWebSite"));
}
}
}

View File

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using InlineConstraints;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Infrastructure;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class InlineConstraintTests
{
private readonly IServiceProvider _provider;
private readonly Action<IBuilder> _app = new Startup().Configure;
private readonly string _jsonConfigFilePath;
private readonly Configuration _config = new Configuration();
public InlineConstraintTests()
{
_provider = TestHelper.CreateServices("InlineConstraintsWebSite");
// TODO: Hardcoding the config file path for now. Update it to read it from args.
_jsonConfigFilePath = @"config\InlineConstraintTestsConfig.json";
_config.AddJsonFile(_jsonConfigFilePath);
Environment.SetEnvironmentVariable("AppConfigPath", _jsonConfigFilePath);
}
[Fact]
public async Task RoutingToANonExistantArea_WithExistConstraint_RoutesToCorrectAction()
{
// Arrange
var source = new JsonConfigurationSource(_jsonConfigFilePath);
// Add the exists inline constraint.
_config.Set("TemplateCollection:areaRoute:TemplateValue",
@"{area:exists}/{controller=Home}/{action=Index}");
_config.Set("TemplateCollection:actionAsMethod:TemplateValue",
@"{controller=Home}/{action=Index}");
_config.Commit();
var server = TestServer.Create(_provider, _app);
var client = server.Handler;
// Act
var result = await client.GetAsync("http://localhost/Users");
Assert.Equal(200, result.StatusCode);
// Assert
var returnValue = await result.ReadBodyAsStringAsync();
Assert.Equal("Users.Index", returnValue);
}
[Fact]
public async Task RoutingToANonExistantArea_WithoutExistConstraint_RoutesToIncorrectAction()
{
// Arrange
_config.Set("TemplateCollection:areaRoute:TemplateValue",
@"{area}/{controller=Home}/{action=Index}");
_config.Set("TemplateCollection:actionAsMethod:TemplateValue",
@"{controller=Home}/{action=Index}");
_config.Commit();
var server = TestServer.Create(_provider, _app);
var client = server.Handler;
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>
(async () => await client.GetAsync("http://localhost/Users"));
Assert.Equal("The view 'Index' was not found." +
" The following locations were searched:\r\n/Areas/Users/Views/Home/Index.cshtml\r\n" +
"/Areas/Users/Views/Shared/Index.cshtml\r\n/Views/Shared/Index.cshtml.",
ex.Message);
}
}
}

View File

@ -30,6 +30,8 @@
<Content Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="InlineConstraintTests.cs" />
<Compile Include="TestHelper.cs" />
<Compile Include="BasicTests.cs" />
<Compile Include="HttpResponseHelpers.cs" />
<Compile Include="ResourceHelpers.cs" />

View File

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using InlineConstraints;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Infrastructure;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public static class TestHelper
{
public static IServiceProvider CreateServices(string applicationWebSiteName)
{
var originalProvider = CallContextServiceLocator.Locator.ServiceProvider;
IApplicationEnvironment appEnvironment = originalProvider.GetService<IApplicationEnvironment>();
// When an application executes in a regular context, the application base path points to the root
// directory where the application is located, for example MvcSample.Web. However, when executing
// an aplication as part of a test, the ApplicationBasePath of the IApplicationEnvironment points
// to the root folder of the test project.
// To compensate for this, we need to calculate the original path and override the application
// environment value so that components like the view engine work properly in the context of the
// test.
string appBasePath = CalculateApplicationBasePath(appEnvironment, applicationWebSiteName);
var provider = new ServiceCollection()
.AddInstance(typeof(IApplicationEnvironment),
new TestApplicationEnvironment(appEnvironment, appBasePath))
.BuildServiceProvider(originalProvider);
return provider;
}
// Calculate the path relative to the application base path.
public static string CalculateApplicationBasePath(IApplicationEnvironment appEnvironment, string applicationWebSiteName)
{
// Mvc/test/Microsoft.AspNet.Mvc.FunctionalTests
var appBase = appEnvironment.ApplicationBasePath;
// Mvc/test
var test = Path.GetDirectoryName(appBase);
// Mvc/test/WebSites/applicationWebSiteName
return Path.GetFullPath(Path.Combine(appBase, "..", "WebSites", applicationWebSiteName));
}
}
}

View File

@ -0,0 +1,10 @@
{
"TemplateCollection": {
"areaRoute": {
"TemplateValue": ""
},
"actionAsMethod": {
"TemplateValue": ""
}
}
}

View File

@ -5,12 +5,14 @@
},
"dependencies": {
"BasicWebSite": "",
"InlineConstraintsWebSite": "",
"Microsoft.AspNet.TestHost": "0.1-alpha-*",
"Microsoft.Framework.Runtime.Interfaces": "0.1-alpha-*",
"xunit.abstractions": "2.0.0-aspnet-*",
"xunit.assert": "2.0.0-aspnet-*",
"xunit.core": "2.0.0-aspnet-*",
"Xunit.KRunner": "0.1-alpha-*"
"Xunit.KRunner": "0.1-alpha-*",
"Microsoft.Framework.ConfigurationModel.Json": "0.1-alpha-*"
},
"commands": {
"test": "Xunit.KRunner"

View File

@ -29,6 +29,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="ActionDescriptorCreationCounter.cs" />
<Compile Include="Controllers\UsersController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\MonitorController.cs" />
<Compile Include="Startup.cs" />

View File

@ -0,0 +1,12 @@
using Microsoft.AspNet.Mvc;
namespace BasicWebSite.Controllers
{
public class UsersController : Controller
{
public IActionResult Index()
{
return Content("Users.Index");
}
}
}

View File

@ -21,8 +21,13 @@ namespace BasicWebSite
// Add MVC to the request pipeline
app.UseMvc(routes =>
{
routes.MapRoute("areaRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" });
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
});
}
}

View File

@ -0,0 +1,10 @@
{
"TemplateCollection" : {
"areaRoute" : {
"TemplateValue" : "{area:exists}/{controller=Home}/{action=Index}"
},
"actionRoute" : {
"TemplateValue" : "{controller=Home}/{action=Index}"
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNet.Mvc;
namespace InlineConstraints.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNet.Mvc;
namespace InlineConstraints.Controllers
{
public class UsersController : Controller
{
public IActionResult Index()
{
return Content("Users.Index");
}
}
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>ea34877f-1ac1-42b7-b4e6-15a093f40cae</ProjectGuid>
<OutputType>Web</OutputType>
</PropertyGroup>
<PropertyGroup Condition="$(OutputType) == 'Console'">
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="$(OutputType) == 'Web'">
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DevelopmentServerPort>38821</DevelopmentServerPort>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DevelopmentServerPort>624661</DevelopmentServerPort>
</PropertyGroup>
<ItemGroup>
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\UsersController.cs" />
<Compile Include="Startup.cs" />
<Compile Include="TestControllerAssemblyProvider.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="App_Data\config.json" />
<Content Include="project.json" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Views\Shared\Error.cshtml" />
<Content Include="Views\Shared\_Layout.cshtml" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;
namespace InlineConstraints
{
public class Startup
{
public Action<IRouteBuilder> RouteCollectionProvider { get; set; }
public void Configure(IBuilder app)
{
// Set up application services
app.UseServices(services =>
{
// Add MVC services to the services container
services.AddMvc();
// Add a custom assembly provider so that we add only controllers present in
// this assembly.
services.AddTransient<IControllerAssemblyProvider, TestControllerAssemblyProvider>();
});
var config = new Configuration();
config.AddEnvironmentVariables();
string appConfigPath;
if (config.TryGet("AppConfigPath", out appConfigPath))
{
config.AddJsonFile(appConfigPath);
}
else
{
var basePath = app.ApplicationServices.GetService<IApplicationEnvironment>().ApplicationBasePath;
config.AddJsonFile(Path.Combine(basePath, @"App_Data\config.json"));
}
// Add MVC to the request pipeline
app.UseMvc(routes=> {
foreach (var item in GetDataFromConfig(config))
{
routes.MapRoute(item.RouteName, item.RouteTemplateValue);
}
});
}
private IEnumerable<RouteConfigData> GetDataFromConfig(IConfiguration config)
{
foreach (var template in config.GetSubKey("TemplateCollection").GetSubKeys())
{
yield return
new RouteConfigData()
{
RouteName = template.Key,
RouteTemplateValue = template.Value.Get("TemplateValue")
};
}
}
private class RouteConfigData
{
public string RouteName { get; set; }
public string RouteTemplateValue { get; set; }
}
}
}

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Mvc;
namespace InlineConstraints
{
public class TestControllerAssemblyProvider : IControllerAssemblyProvider
{
public IEnumerable<Assembly> CandidateAssemblies
{
get
{
return new[] { typeof(TestControllerAssemblyProvider).GetTypeInfo().Assembly };
}
}
}
}

View File

@ -0,0 +1,8 @@
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Inline Constraints Home Page";
}
<div>
<h1>ASP.NET vNext</h1>
</div>

View File

@ -0,0 +1,7 @@
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Error";
}
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
</head>
<body>
<div>
<div>
<div>
@Html.ActionLink("InlineConstraintsWebApplication", "Index", "Home", new { area = "" })
</div>
<div>
<ul>
<li>@Html.ActionLink("Home", "Index", "Home")</li>
</ul>
</div>
</div>
</div>
<div>
@RenderBody()
<hr />
<footer>
<p>&copy; 2014 - My ASP.NET Application</p>
</footer>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
{
"dependencies": {
"Helios": "0.1-alpha-*",
"Microsoft.AspNet.Mvc": "",
"Microsoft.Framework.ConfigurationModel.Json": "0.1-alpha-*"
},
"configurations": {
"net45": { },
"k10": { }
}
}