From c1112fcaf1bab866f3e7c27232250c94f122cc73 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 10 Jul 2014 16:49:00 -0700 Subject: [PATCH] Move ValueProviders to MvcOptions Fixes #778 --- Mvc.sln | 13 ++++ src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs | 6 ++ .../DefaultActionBindingContextProvider.cs | 5 +- src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs | 8 ++- src/Microsoft.AspNet.Mvc/MvcServices.cs | 4 -- ...Microsoft.AspNet.Mvc.FunctionalTests.kproj | 1 + .../ValueProviderTests.cs | 68 +++++++++++++++++++ .../project.json | 1 + .../MvcOptionSetupTest.cs | 18 +++++ .../CustomValueProviderFactory.cs | 39 +++++++++++ .../ValueProvidersSite/HomeController.cs | 26 +++++++ test/WebSites/ValueProvidersSite/Startup.cs | 31 +++++++++ .../ValueProvidersSite.kproj | 34 ++++++++++ test/WebSites/ValueProvidersSite/project.json | 12 ++++ 14 files changed, 259 insertions(+), 7 deletions(-) create mode 100644 test/Microsoft.AspNet.Mvc.FunctionalTests/ValueProviderTests.cs create mode 100644 test/WebSites/ValueProvidersSite/CustomValueProviderFactory.cs create mode 100644 test/WebSites/ValueProvidersSite/HomeController.cs create mode 100644 test/WebSites/ValueProvidersSite/Startup.cs create mode 100644 test/WebSites/ValueProvidersSite/ValueProvidersSite.kproj create mode 100644 test/WebSites/ValueProvidersSite/project.json diff --git a/Mvc.sln b/Mvc.sln index 212b9a95ad..14f6eef714 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -51,6 +51,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Test", EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CompositeViewEngine", "test\WebSites\CompositeViewEngine\CompositeViewEngine.kproj", "{A853B2BA-4449-4908-A416-5A3C027FC22B}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValueProvidersSite", "test\WebSites\ValueProvidersSite\ValueProvidersSite.kproj", "{14F79E79-AE79-48FA-95DE-D794EF4EABB3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -261,6 +263,16 @@ Global {A853B2BA-4449-4908-A416-5A3C027FC22B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {A853B2BA-4449-4908-A416-5A3C027FC22B}.Release|Mixed Platforms.Build.0 = Release|Any CPU {A853B2BA-4449-4908-A416-5A3C027FC22B}.Release|x86.ActiveCfg = Release|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|x86.ActiveCfg = Debug|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Any CPU.Build.0 = Release|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -287,5 +299,6 @@ Global {42CDBF4A-E238-4C0F-A416-44588363EB4C} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {5F945B82-FE5F-425C-956C-8BC2F2020254} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {14F79E79-AE79-48FA-95DE-D794EF4EABB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index b9ca779677..af5515b082 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -23,6 +23,7 @@ namespace Microsoft.AspNet.Mvc ApplicationModelConventions = new List(); ModelBinders = new List(); ViewEngines = new List(); + ValueProviderFactories = new List(); } /// @@ -81,6 +82,11 @@ namespace Microsoft.AspNet.Mvc /// public List ViewEngines { get; private set; } + /// + /// Gets a list of used by this application. + /// + public List ValueProviderFactories { get; private set; } + public List ApplicationModelConventions { get; private set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs index cfea960128..1b903f85a3 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Mvc { @@ -20,13 +21,13 @@ namespace Microsoft.AspNet.Mvc public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider, ICompositeModelBinder compositeModelBinder, - IEnumerable valueProviderFactories, + IOptionsAccessor mvcOptionsAccessor, IInputFormatterProvider inputFormatterProvider, IEnumerable validatorProviders) { _modelMetadataProvider = modelMetadataProvider; _compositeModelBinder = compositeModelBinder; - _valueProviderFactories = valueProviderFactories; + _valueProviderFactories = mvcOptionsAccessor.Options.ValueProviderFactories; _inputFormatterProvider = inputFormatterProvider; _validatorProviders = validatorProviders; } diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index 7834185b30..9faba693da 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -13,9 +13,10 @@ namespace Microsoft.AspNet.Mvc public class MvcOptionsSetup : IOptionsSetup { /// + /// Order is -1 to allow MvcOptionsSetup to run before a user call to SetupOptions. public int Order { - get { return 1; } + get { return -1; } } /// @@ -30,6 +31,11 @@ namespace Microsoft.AspNet.Mvc options.ModelBinders.Add(typeof(GenericModelBinder)); options.ModelBinders.Add(new MutableObjectModelBinder()); options.ModelBinders.Add(new ComplexModelDtoModelBinder()); + + // Set up ValueProviders + options.ValueProviderFactories.Add(new RouteValueValueProviderFactory()); + options.ValueProviderFactories.Add(new QueryStringValueProviderFactory()); + options.ValueProviderFactories.Add(new FormValueProviderFactory()); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 5e703bdf40..9e9a02e729 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -59,10 +59,6 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); yield return describe.Scoped(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); yield return describe.Transient(); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj index ee527ae290..76668c07e9 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj @@ -31,6 +31,7 @@ + diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ValueProviderTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ValueProviderTests.cs new file mode 100644 index 0000000000..2fae97fa1d --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ValueProviderTests.cs @@ -0,0 +1,68 @@ +// 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.Threading.Tasks; +using ValueProvidersSite; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class ValueProviderTest + { + private readonly IServiceProvider _services; + private readonly Action _app = new Startup().Configure; + + public ValueProviderTest() + { + _services = TestHelper.CreateServices("ValueProvidersSite"); + } + + [Fact] + public async Task ValueProviderFactories_AreVisitedInSequentialOrder_ForValueProviders() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.Handler; + + // Act + var response = await client.GetAsync("http://localhost/Home/TestValueProvider?test=not-test-value"); + + // Assert + var body = await response.ReadBodyAsStringAsync(); + Assert.Equal("custom-value-provider-value", body.Trim()); + } + + [Fact] + public async Task ValueProviderFactories_ReturnsValuesFromQueryValueProvider() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.Handler; + + // Act + var response = await client.GetAsync("http://localhost/Home/DefaultValueProviders?test=query-value"); + + // Assert + var body = await response.ReadBodyAsStringAsync(); + Assert.Equal("query-value", body.Trim()); + } + + [Fact] + public async Task ValueProviderFactories_ReturnsValuesFromRouteValueProvider() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.Handler; + + // Act + var response = await client.GetAsync("http://localhost/RouteTest/route-value"); + + // Assert + var body = await response.ReadBodyAsStringAsync(); + Assert.Equal("route-value", body.Trim()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 1b43111ff2..32e567943e 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -14,6 +14,7 @@ "Microsoft.Framework.DependencyInjection": "1.0.0-*", "Microsoft.Framework.Runtime.Interfaces": "1.0.0-*", "RoutingWebSite": "", + "ValueProvidersSite": "", "Xunit.KRunner": "1.0.0-*" }, "commands": { diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs index 78fc774376..d256168a7a 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs @@ -42,5 +42,23 @@ namespace Microsoft.AspNet.Mvc Assert.Equal(typeof(MutableObjectModelBinder), mvcOptions.ModelBinders[3].ModelBinderType); Assert.Equal(typeof(ComplexModelDtoModelBinder), mvcOptions.ModelBinders[4].ModelBinderType); } + + [Fact] + public void Setup_SetsUpValueProviders() + { + // Arrange + var mvcOptions = new MvcOptions(); + var setup = new MvcOptionsSetup(); + + // Act + setup.Setup(mvcOptions); + + // Assert + var valueProviders = mvcOptions.ValueProviderFactories; + Assert.Equal(3, valueProviders.Count); + Assert.IsType(valueProviders[0]); + Assert.IsType(valueProviders[1]); + Assert.IsType(valueProviders[2]); + } } } \ No newline at end of file diff --git a/test/WebSites/ValueProvidersSite/CustomValueProviderFactory.cs b/test/WebSites/ValueProvidersSite/CustomValueProviderFactory.cs new file mode 100644 index 0000000000..2c482ace11 --- /dev/null +++ b/test/WebSites/ValueProvidersSite/CustomValueProviderFactory.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace ValueProvidersSite +{ + public class CustomValueProviderFactory : IValueProviderFactory + { + public IValueProvider GetValueProvider(ValueProviderFactoryContext context) + { + if (context.HttpContext.Request.Path.Value.Contains("TestValueProvider")) + { + return new CustomValueProvider(); + } + + return null; + } + + private class CustomValueProvider : IValueProvider + { + public Task ContainsPrefixAsync(string prefix) + { + var result = string.Equals(prefix, "test", StringComparison.OrdinalIgnoreCase); + return Task.FromResult(result); + } + + public Task GetValueAsync(string key) + { + var value = "custom-value-provider-value"; + var result = new ValueProviderResult(value, value, CultureInfo.CurrentCulture); + return Task.FromResult(result); + } + } + } +} \ No newline at end of file diff --git a/test/WebSites/ValueProvidersSite/HomeController.cs b/test/WebSites/ValueProvidersSite/HomeController.cs new file mode 100644 index 0000000000..a6255dfd3d --- /dev/null +++ b/test/WebSites/ValueProvidersSite/HomeController.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc; + +namespace ValueProvidersSite +{ + public class HomeController + { + public string TestValueProvider(string test) + { + return test; + } + + public string DefaultValueProviders(string test) + { + return test; + } + + [HttpGet("/RouteTest/{test}")] + public string RouteValueProviders(string test) + { + return test; + } + } +} \ No newline at end of file diff --git a/test/WebSites/ValueProvidersSite/Startup.cs b/test/WebSites/ValueProvidersSite/Startup.cs new file mode 100644 index 0000000000..fe50940e76 --- /dev/null +++ b/test/WebSites/ValueProvidersSite/Startup.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Mvc; +using Microsoft.Framework.DependencyInjection; + +namespace ValueProvidersSite +{ + public class Startup + { + public void Configure(IBuilder app) + { + var configuration = app.GetTestConfiguration(); + + // Set up application services + app.UseServices(services => + { + // Add MVC services to the services container + services.AddMvc(configuration) + .SetupOptions(options => + { + options.ValueProviderFactories.Insert(1, new CustomValueProviderFactory()); + }); + }); + + // Add MVC to the request pipeline + app.UseMvc(); + } + } +} diff --git a/test/WebSites/ValueProvidersSite/ValueProvidersSite.kproj b/test/WebSites/ValueProvidersSite/ValueProvidersSite.kproj new file mode 100644 index 0000000000..f652f49abf --- /dev/null +++ b/test/WebSites/ValueProvidersSite/ValueProvidersSite.kproj @@ -0,0 +1,34 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 14f79e79-ae79-48fa-95de-d794ef4eabb3 + Library + + + ConsoleDebugger + + + WebDebugger + + + + + + + 2.0 + + + + + + + + + + + \ No newline at end of file diff --git a/test/WebSites/ValueProvidersSite/project.json b/test/WebSites/ValueProvidersSite/project.json new file mode 100644 index 0000000000..8c1ff28de3 --- /dev/null +++ b/test/WebSites/ValueProvidersSite/project.json @@ -0,0 +1,12 @@ +{ + "dependencies": { + "Microsoft.AspNet.Mvc": "", + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Mvc.TestConfiguration": "", + "Microsoft.Framework.ConfigurationModel": "" + }, + "configurations": { + "net45": { }, + "k10": { } + } +}