From 284e968101a5e4339541954b2218a53083ba0fd5 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 7 Mar 2019 18:13:02 -0800 Subject: [PATCH] Add AddControllers and AddRazorPages These are *new style* for configuring MVC in services. We're adding these to make things feel a bit more tailored to those particular scenarios. ---- The main reason for this is that we've had repeated community asks for an *API-optimized* way of configuring MVC. I don't think that using AddMvcCore is a suitable building block, because it has too many options that you want. I've think I've identified the reasonable set of features that should be part of the default experience for APIs. All of these things are already pay-for-play and are activated by the presence of attributes. The only additional cost is loading of assemblies and a few additional inspections of the attributes (cached). ---- Additionally the AddControllers experience is composible. You can add views to it, or add pages and get the whole thing. AddRazorPages is basically an alias for what AddMvc does today. We don't currently have a way to add pages without controllers (the opposite is true). Based on feedback we could specialize this more. ---- Branding and perception are important, and we've users ask for more flexibility in what gets added. The plan is to update the templates to use this experience in preview4, and see what kind of feedback we get. --- ...ontrollerEndpointRouteBuilderExtensions.cs | 2 +- .../MvcRazorMvcCoreBuilderExtensions.cs | 14 +- .../Mvc.Razor/src/Properties/AssemblyInfo.cs | 1 + ...azorPagesEndpointRouteBuilderExtensions.cs | 20 +- .../MvcRazorPagesMvcCoreBuilderExtensions.cs | 6 +- .../src/Properties/AssemblyInfo.cs | 2 + .../TagHelperExtensions.cs | 19 +- .../src/Properties/AssemblyInfo.cs | 1 + ...MvcViewFeaturesMvcCoreBuilderExtensions.cs | 2 +- .../Mvc.ViewFeatures/src/PartialViewResult.cs | 10 +- .../src/Properties/AssemblyInfo.cs | 1 + .../src/ViewComponentResult.cs | 10 +- src/Mvc/Mvc.ViewFeatures/src/ViewResult.cs | 10 +- .../test/PartialViewResultTest.cs | 19 ++ .../test/ViewComponentResultTest.cs | 19 ++ .../Mvc.ViewFeatures/test/ViewResultTest.cs | 19 ++ .../Microsoft.AspNetCore.Mvc.netcoreapp3.0.cs | 6 + .../Mvc/src/MvcServiceCollectionExtensions.cs | 317 ++++++++++++++++-- .../MvcServiceCollectionExtensionsTest.cs | 145 +++++++- .../WebSites/ApiExplorerWebSite/Startup.cs | 2 +- .../ApplicationModelWebSite/Startup.cs | 4 +- .../ControllersFromServicesWebSite/Startup.cs | 2 +- src/Mvc/test/WebSites/CorsWebSite/Startup.cs | 2 +- .../ErrorPageMiddlewareWebSite/Startup.cs | 2 +- src/Mvc/test/WebSites/FilesWebSite/Startup.cs | 2 +- .../test/WebSites/FormatterWebSite/Startup.cs | 2 +- .../StartupWithRespectBrowserAcceptHeader.cs | 2 +- .../WebSites/GenericHostWebSite/Startup.cs | 2 +- .../WebSites/RazorPagesWebSite/Startup.cs | 3 +- .../test/WebSites/SecurityWebSite/Startup.cs | 3 +- .../WebSites/TagHelpersWebSite/Startup.cs | 2 +- .../WebSites/VersioningWebSite/Startup.cs | 3 +- .../WebSites/XmlFormattersWebSite/Startup.cs | 2 +- .../RazorComponentsWeb-CSharp/Startup.cs | 2 +- .../content/RazorPagesWeb-CSharp/Startup.cs | 4 +- .../content/StarterWeb-CSharp/Startup.cs | 5 +- .../content/StarterWeb-FSharp/Startup.fs | 3 +- .../content/WebApi-CSharp/Startup.cs | 2 +- .../content/WebApi-FSharp/Startup.fs | 2 +- 39 files changed, 564 insertions(+), 110 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs b/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs index 03d25e70c2..726988fd6f 100644 --- a/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs +++ b/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs @@ -450,7 +450,7 @@ namespace Microsoft.AspNetCore.Builder { throw new InvalidOperationException(Resources.FormatUnableToFindServices( nameof(IServiceCollection), - "AddMvc", + "AddControllers", "ConfigureServices(...)")); } } diff --git a/src/Mvc/Mvc.Razor/src/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.Razor/src/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs index fdd018eca7..fc94c6d209 100644 --- a/src/Mvc/Mvc.Razor/src/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.Razor/src/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs @@ -28,7 +28,7 @@ namespace Microsoft.Extensions.DependencyInjection } builder.AddViews(); - AddRazorViewEngineFeatureProviders(builder); + AddRazorViewEngineFeatureProviders(builder.PartManager); AddRazorViewEngineServices(builder.Services); return builder; } @@ -49,7 +49,7 @@ namespace Microsoft.Extensions.DependencyInjection builder.AddViews(); - AddRazorViewEngineFeatureProviders(builder); + AddRazorViewEngineFeatureProviders(builder.PartManager); AddRazorViewEngineServices(builder.Services); builder.Services.Configure(setupAction); @@ -57,20 +57,20 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } - private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder) + internal static void AddRazorViewEngineFeatureProviders(ApplicationPartManager partManager) { - if (!builder.PartManager.FeatureProviders.OfType().Any()) + if (!partManager.FeatureProviders.OfType().Any()) { - builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider()); + partManager.FeatureProviders.Add(new TagHelperFeatureProvider()); } // ViewFeature items have precedence semantics - when two views have the same path \ identifier, // the one that appears earlier in the list wins. Therefore the ordering of // RazorCompiledItemFeatureProvider and ViewsFeatureProvider is pertinent - any view compiled // using the Sdk will be preferred to views compiled using MvcPrecompilation. - if (!builder.PartManager.FeatureProviders.OfType().Any()) + if (!partManager.FeatureProviders.OfType().Any()) { - builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider()); + partManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider()); } } diff --git a/src/Mvc/Mvc.Razor/src/Properties/AssemblyInfo.cs b/src/Mvc/Mvc.Razor/src/Properties/AssemblyInfo.cs index db40b79a34..3d28953a21 100644 --- a/src/Mvc/Mvc.Razor/src/Properties/AssemblyInfo.cs +++ b/src/Mvc/Mvc.Razor/src/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TagHelpers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs b/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs index 90a9dd0657..22b38e041c 100644 --- a/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs +++ b/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs @@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Builder /// /// does not re-execute routing, and will /// not generate route values based on routes defined elsewhere. When using this overload, the path route value - /// will be available. + /// will be available. /// /// public static void MapFallbackToPage(this IEndpointRouteBuilder endpoints, string page) @@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Builder GetOrCreateDataSource(endpoints); // Maps a fallback endpoint with an empty delegate. This is OK because - // we don't expect the delegate to run. + // we don't expect the delegate to run. endpoints.MapFallback(context => Task.CompletedTask).Add(b => { // MVC registers a policy that looks for this metadata. @@ -109,8 +109,8 @@ namespace Microsoft.AspNetCore.Builder /// /// /// does not re-execute routing, and will - /// not generate route values based on routes defined elsewhere. When using this overload, the route values provided by matching - /// will be available. + /// not generate route values based on routes defined elsewhere. When using this overload, the route values provided by matching + /// will be available. /// /// public static void MapFallbackToPage( @@ -141,7 +141,7 @@ namespace Microsoft.AspNetCore.Builder GetOrCreateDataSource(endpoints); // Maps a fallback endpoint with an empty delegate. This is OK because - // we don't expect the delegate to run. + // we don't expect the delegate to run. endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b => { // MVC registers a policy that looks for this metadata. @@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Builder /// /// does not re-execute routing, and will /// not generate route values based on routes defined elsewhere. When using this overload, the path route value - /// will be available. + /// will be available. /// /// public static void MapFallbackToAreaPage( @@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Builder GetOrCreateDataSource(endpoints); // Maps a fallback endpoint with an empty delegate. This is OK because - // we don't expect the delegate to run. + // we don't expect the delegate to run. endpoints.MapFallback(context => Task.CompletedTask).Add(b => { // MVC registers a policy that looks for this metadata. @@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.Builder /// /// /// does not re-execute routing, and will - /// not generate route values based on routes defined elsewhere. When using this overload, the route values provided by matching + /// not generate route values based on routes defined elsewhere. When using this overload, the route values provided by matching /// will be available. /// /// @@ -263,7 +263,7 @@ namespace Microsoft.AspNetCore.Builder GetOrCreateDataSource(endpoints); // Maps a fallback endpoint with an empty delegate. This is OK because - // we don't expect the delegate to run. + // we don't expect the delegate to run. endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b => { // MVC registers a policy that looks for this metadata. @@ -287,7 +287,7 @@ namespace Microsoft.AspNetCore.Builder { throw new InvalidOperationException(Mvc.Core.Resources.FormatUnableToFindServices( nameof(IServiceCollection), - "AddMvc", + "AddRazorPages", "ConfigureServices(...)")); } } diff --git a/src/Mvc/Mvc.RazorPages/src/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.RazorPages/src/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs index bc62660748..6a4f62dff3 100644 --- a/src/Mvc/Mvc.RazorPages/src/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.RazorPages/src/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs @@ -26,7 +26,7 @@ namespace Microsoft.Extensions.DependencyInjection builder.AddRazorViewEngine(); - AddServices(builder.Services); + AddRazorPagesServices(builder.Services); return builder; } @@ -47,7 +47,7 @@ namespace Microsoft.Extensions.DependencyInjection builder.AddRazorViewEngine(); - AddServices(builder.Services); + AddRazorPagesServices(builder.Services); builder.Services.Configure(setupAction); @@ -77,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection } // Internal for testing. - internal static void AddServices(IServiceCollection services) + internal static void AddRazorPagesServices(IServiceCollection services) { // Options services.TryAddEnumerable( diff --git a/src/Mvc/Mvc.RazorPages/src/Properties/AssemblyInfo.cs b/src/Mvc/Mvc.RazorPages/src/Properties/AssemblyInfo.cs index 5f7daf43f9..0d51e4a81b 100644 --- a/src/Mvc/Mvc.RazorPages/src/Properties/AssemblyInfo.cs +++ b/src/Mvc/Mvc.RazorPages/src/Properties/AssemblyInfo.cs @@ -3,6 +3,8 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] + [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Mvc/Mvc.TagHelpers/src/DependencyInjection/TagHelperExtensions.cs b/src/Mvc/Mvc.TagHelpers/src/DependencyInjection/TagHelperExtensions.cs index e39242536a..74b3ef5853 100644 --- a/src/Mvc/Mvc.TagHelpers/src/DependencyInjection/TagHelperExtensions.cs +++ b/src/Mvc/Mvc.TagHelpers/src/DependencyInjection/TagHelperExtensions.cs @@ -25,13 +25,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(builder)); } - builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); - - // Required default services for cache tag helpers - builder.Services.AddDistributedMemoryCache(); - builder.Services.TryAddSingleton(); + AddCacheTagHelperServices(builder.Services); return builder; } @@ -81,5 +75,16 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } + + internal static void AddCacheTagHelperServices(IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + + // Required default services for cache tag helpers + services.AddDistributedMemoryCache(); + services.TryAddSingleton(); + } } } \ No newline at end of file diff --git a/src/Mvc/Mvc.TagHelpers/src/Properties/AssemblyInfo.cs b/src/Mvc/Mvc.TagHelpers/src/Properties/AssemblyInfo.cs index 4516d25702..bfd89af653 100644 --- a/src/Mvc/Mvc.TagHelpers/src/Properties/AssemblyInfo.cs +++ b/src/Mvc/Mvc.TagHelpers/src/Properties/AssemblyInfo.cs @@ -3,5 +3,6 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TagHelpers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs index 285ba5aec5..faeeebb79d 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs @@ -63,7 +63,7 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } - private static void AddViewComponentApplicationPartsProviders(ApplicationPartManager manager) + internal static void AddViewComponentApplicationPartsProviders(ApplicationPartManager manager) { if (!manager.FeatureProviders.OfType().Any()) { diff --git a/src/Mvc/Mvc.ViewFeatures/src/PartialViewResult.cs b/src/Mvc/Mvc.ViewFeatures/src/PartialViewResult.cs index 56b0f80139..3213e16c28 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/PartialViewResult.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/PartialViewResult.cs @@ -65,7 +65,15 @@ namespace Microsoft.AspNetCore.Mvc } var services = context.HttpContext.RequestServices; - var executor = services.GetRequiredService>(); + var executor = services.GetService>(); + if (executor == null) + { + throw new InvalidOperationException(Mvc.Core.Resources.FormatUnableToFindServices( + nameof(IServiceCollection), + "AddControllersWithViews()", + "ConfigureServices(...)")); + } + return executor.ExecuteAsync(context, this); } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/Properties/AssemblyInfo.cs b/src/Mvc/Mvc.ViewFeatures/src/Properties/AssemblyInfo.cs index 01241ba199..b677034d2d 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/Properties/AssemblyInfo.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TagHelpers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Mvc/Mvc.ViewFeatures/src/ViewComponentResult.cs b/src/Mvc/Mvc.ViewFeatures/src/ViewComponentResult.cs index 433f44eff4..f4d2385ea5 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/ViewComponentResult.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/ViewComponentResult.cs @@ -64,7 +64,15 @@ namespace Microsoft.AspNetCore.Mvc } var services = context.HttpContext.RequestServices; - var executor = services.GetRequiredService>(); + var executor = services.GetService>(); + if (executor == null) + { + throw new InvalidOperationException(Mvc.Core.Resources.FormatUnableToFindServices( + nameof(IServiceCollection), + "AddControllersWithViews()", + "ConfigureServices(...)")); + } + return executor.ExecuteAsync(context, this); } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/ViewResult.cs b/src/Mvc/Mvc.ViewFeatures/src/ViewResult.cs index 5534ec8666..d99aaefa0d 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/ViewResult.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/ViewResult.cs @@ -64,7 +64,15 @@ namespace Microsoft.AspNetCore.Mvc throw new ArgumentNullException(nameof(context)); } - var executor = context.HttpContext.RequestServices.GetRequiredService>(); + var executor = context.HttpContext.RequestServices.GetService>(); + if (executor == null) + { + throw new InvalidOperationException(Mvc.Core.Resources.FormatUnableToFindServices( + nameof(IServiceCollection), + "AddControllersWithViews()", + "ConfigureServices(...)")); + } + await executor.ExecuteAsync(context, this); } } diff --git a/src/Mvc/Mvc.ViewFeatures/test/PartialViewResultTest.cs b/src/Mvc/Mvc.ViewFeatures/test/PartialViewResultTest.cs index a78e3a103c..c80077b088 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/PartialViewResultTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/PartialViewResultTest.cs @@ -42,6 +42,25 @@ namespace Microsoft.AspNetCore.Mvc Assert.Same(customModel, viewResult.Model); } + [Fact] + public async Task ExecuteResultAsync_Throws_IfServicesNotRegistered() + { + // Arrange + var actionContext = new ActionContext(new DefaultHttpContext() { RequestServices = Mock.Of(), }, new RouteData(), new ActionDescriptor()); + var expected = + $"Unable to find the required services. Please add all the required services by calling " + + $"'IServiceCollection.AddControllersWithViews()' inside the call to 'ConfigureServices(...)' " + + $"in the application startup code."; + + var viewResult = new PartialViewResult(); + + // Act + var ex = await Assert.ThrowsAsync(() => viewResult.ExecuteResultAsync(actionContext)); + + // Assert + Assert.Equal(expected, ex.Message); + } + [Fact] public async Task ExecuteResultAsync_Throws_IfViewCouldNotBeFound_MessageUsesGetViewLocations() { diff --git a/src/Mvc/Mvc.ViewFeatures/test/ViewComponentResultTest.cs b/src/Mvc/Mvc.ViewFeatures/test/ViewComponentResultTest.cs index ebc324dbea..2c65acccb2 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/ViewComponentResultTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/ViewComponentResultTest.cs @@ -52,6 +52,25 @@ namespace Microsoft.AspNetCore.Mvc Assert.Same(customModel, viewResult.Model); } + [Fact] + public async Task ExecuteResultAsync_Throws_IfServicesNotRegistered() + { + // Arrange + var actionContext = new ActionContext(new DefaultHttpContext() { RequestServices = Mock.Of(), }, new RouteData(), new ActionDescriptor()); + var expected = + $"Unable to find the required services. Please add all the required services by calling " + + $"'IServiceCollection.AddControllersWithViews()' inside the call to 'ConfigureServices(...)' " + + $"in the application startup code."; + + var viewResult = new ViewComponentResult(); + + // Act + var ex = await Assert.ThrowsAsync(() => viewResult.ExecuteResultAsync(actionContext)); + + // Assert + Assert.Equal(expected, ex.Message); + } + [Fact] public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData() { diff --git a/src/Mvc/Mvc.ViewFeatures/test/ViewResultTest.cs b/src/Mvc/Mvc.ViewFeatures/test/ViewResultTest.cs index c05b10c071..fbb203f8d5 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/ViewResultTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/ViewResultTest.cs @@ -52,6 +52,25 @@ namespace Microsoft.AspNetCore.Mvc Assert.Same(customModel, viewResult.Model); } + [Fact] + public async Task ExecuteResultAsync_Throws_IfServicesNotRegistered() + { + // Arrange + var actionContext = new ActionContext(new DefaultHttpContext() { RequestServices = Mock.Of(), }, new RouteData(), new ActionDescriptor()); + var expected = + $"Unable to find the required services. Please add all the required services by calling " + + $"'IServiceCollection.AddControllersWithViews()' inside the call to 'ConfigureServices(...)' " + + $"in the application startup code."; + + var viewResult = new ViewResult(); + + // Act + var ex = await Assert.ThrowsAsync(() => viewResult.ExecuteResultAsync(actionContext)); + + // Assert + Assert.Equal(expected, ex.Message); + } + [Fact] public async Task ExecuteResultAsync_Throws_IfViewCouldNotBeFound_MessageUsesGetViewLocations() { diff --git a/src/Mvc/Mvc/ref/Microsoft.AspNetCore.Mvc.netcoreapp3.0.cs b/src/Mvc/Mvc/ref/Microsoft.AspNetCore.Mvc.netcoreapp3.0.cs index bf170d8931..4ee7b115b5 100644 --- a/src/Mvc/Mvc/ref/Microsoft.AspNetCore.Mvc.netcoreapp3.0.cs +++ b/src/Mvc/Mvc/ref/Microsoft.AspNetCore.Mvc.netcoreapp3.0.cs @@ -5,7 +5,13 @@ namespace Microsoft.Extensions.DependencyInjection { public static partial class MvcServiceCollectionExtensions { + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddControllers(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddControllers(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddControllersWithViews(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddControllersWithViews(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddMvc(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddMvc(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddRazorPages(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddRazorPages(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) { throw null; } } } diff --git a/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs b/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs index eb045d1900..ffb69ee504 100644 --- a/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs +++ b/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs @@ -9,6 +9,7 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.TagHelpers; namespace Microsoft.Extensions.DependencyInjection @@ -30,43 +31,8 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(services)); } - var builder = services.AddMvcCore(); - - builder.AddApiExplorer(); - builder.AddAuthorization(); - - AddDefaultFrameworkParts(builder.PartManager); - - // Order added affects options setup order - - // Default framework order - builder.AddFormatterMappings(); - builder.AddViews(); - builder.AddRazorViewEngine(); - builder.AddRazorPages(); - builder.AddCacheTagHelper(); - - // +1 order - builder.AddDataAnnotations(); // +1 order - - builder.AddCors(); - - return new MvcBuilder(builder.Services, builder.PartManager); - } - - private static void AddDefaultFrameworkParts(ApplicationPartManager partManager) - { - var mvcTagHelpersAssembly = typeof(InputTagHelper).GetTypeInfo().Assembly; - if (!partManager.ApplicationParts.OfType().Any(p => p.Assembly == mvcTagHelpersAssembly)) - { - partManager.ApplicationParts.Add(new FrameworkAssemblyPart(mvcTagHelpersAssembly)); - } - - var mvcRazorAssembly = typeof(UrlResolutionTagHelper).GetTypeInfo().Assembly; - if (!partManager.ApplicationParts.OfType().Any(p => p.Assembly == mvcRazorAssembly)) - { - partManager.ApplicationParts.Add(new FrameworkAssemblyPart(mvcRazorAssembly)); - } + services.AddControllersWithViews(); + return services.AddRazorPages(); } /// @@ -93,6 +59,283 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } + /// + /// Adds services for controllers to the specified . This method will not + /// register services used for views or pages. + /// + /// The to add services to. + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features with controllers for an API. This + /// combines the effects of , + /// , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for controllers with views call + /// on the resulting builder. + /// + /// + /// To add services for pages call + /// on the resulting builder. + /// + /// + public static IMvcBuilder AddControllers(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + var builder = AddControllersCore(services); + return new MvcBuilder(builder.Services, builder.PartManager); + } + + /// + /// Adds services for controllers to the specified . This method will not + /// register services used for views or pages. + /// + /// The to add services to. + /// An to configure the provided . + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features with controllers for an API. This + /// combines the effects of , + /// , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for controllers with views call + /// on the resulting builder. + /// + /// + /// To add services for pages call + /// on the resulting builder. + /// + /// + public static IMvcBuilder AddControllers(this IServiceCollection services, Action configure) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + // This method excludes all of the view-related services by default. + var builder = AddControllersCore(services); + if (configure != null) + { + builder.AddMvcOptions(configure); + } + + return new MvcBuilder(builder.Services, builder.PartManager); + } + + private static IMvcCoreBuilder AddControllersCore(IServiceCollection services) + { + // This method excludes all of the view-related services by default. + return services + .AddMvcCore() + .AddApiExplorer() + .AddAuthorization() + .AddCors() + .AddDataAnnotations() + .AddFormatterMappings(); + } + + /// + /// Adds services for controllers to the specified . This method will not + /// register services used for pages. + /// + /// The to add services to. + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features with controllers with views. This + /// combines the effects of , + /// , + /// , + /// , + /// , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for pages call . + /// + /// + public static IMvcBuilder AddControllersWithViews(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + var builder = AddControllersWithViewsCore(services); + return new MvcBuilder(builder.Services, builder.PartManager); + } + + /// + /// Adds services for controllers to the specified . This method will not + /// register services used for pages. + /// + /// The to add services to. + /// An to configure the provided . + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features with controllers with views. This + /// combines the effects of , + /// , + /// , + /// , + /// , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for pages call . + /// + /// + public static IMvcBuilder AddControllersWithViews(this IServiceCollection services, Action configure) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + // This method excludes all of the view-related services by default. + var builder = AddControllersWithViewsCore(services); + if (configure != null) + { + builder.AddMvcOptions(configure); + } + + return new MvcBuilder(builder.Services, builder.PartManager); + } + + private static IMvcCoreBuilder AddControllersWithViewsCore(IServiceCollection services) + { + var builder = AddControllersCore(services).AddViews().AddRazorViewEngine(); + + AddTagHelpersFrameworkParts(builder.PartManager); + + return builder; + } + + /// + /// Adds services for pages to the specified . + /// + /// The to add services to. + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features for pages. This + /// combines the effects of , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for controllers for APIs call . + /// + /// + /// To add services for controllers with views call . + /// + /// + public static IMvcBuilder AddRazorPages(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + var builder = AddRazorPagesCore(services); + return new MvcBuilder(builder.Services, builder.PartManager); + } + + /// + /// Adds services for pages to the specified . + /// + /// The to add services to. + /// An to configure the provided . + /// An that can be used to further configure the MVC services. + /// + /// + /// This method configures the MVC services for the commonly used features for pages. This + /// combines the effects of , + /// , + /// , + /// , + /// and . + /// + /// + /// To add services for controllers for APIs call . + /// + /// + /// To add services for controllers with views call . + /// + /// + public static IMvcBuilder AddRazorPages(this IServiceCollection services, Action configure) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + var builder = AddRazorPagesCore(services); + if (configure != null) + { + builder.AddRazorPages(configure); + } + + return new MvcBuilder(builder.Services, builder.PartManager); + + } + + private static IMvcCoreBuilder AddRazorPagesCore(IServiceCollection services) + { + // This method includes the minimal things controllers need. It's not really feasible to exclude the services + // for controllers. + var builder = services + .AddMvcCore() + .AddAuthorization() + .AddDataAnnotations() + .AddRazorPages() + .AddCacheTagHelper(); + + AddTagHelpersFrameworkParts(builder.PartManager); + + return builder; + } + + internal static void AddTagHelpersFrameworkParts(ApplicationPartManager partManager) + { + var mvcTagHelpersAssembly = typeof(InputTagHelper).GetTypeInfo().Assembly; + if (!partManager.ApplicationParts.OfType().Any(p => p.Assembly == mvcTagHelpersAssembly)) + { + partManager.ApplicationParts.Add(new FrameworkAssemblyPart(mvcTagHelpersAssembly)); + } + + var mvcRazorAssembly = typeof(UrlResolutionTagHelper).GetTypeInfo().Assembly; + if (!partManager.ApplicationParts.OfType().Any(p => p.Assembly == mvcRazorAssembly)) + { + partManager.ApplicationParts.Add(new FrameworkAssemblyPart(mvcRazorAssembly)); + } + } + [DebuggerDisplay("{Name}")] private class FrameworkAssemblyPart : AssemblyPart, ICompilationReferencesProvider { diff --git a/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs b/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs index aad217683c..c7dadf76b5 100644 --- a/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs +++ b/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs @@ -17,7 +17,6 @@ using Microsoft.AspNetCore.Mvc.Cors; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.TagHelpers; @@ -27,11 +26,8 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures.Filters; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; using Moq; -using Newtonsoft.Json.Serialization; using Xunit; namespace Microsoft.AspNetCore.Mvc @@ -45,23 +41,32 @@ namespace Microsoft.AspNetCore.Mvc // For these kind of multi registration service types, we want to make sure that MVC will still add its // services if the implementation type is different. [Fact] - public void MultiRegistrationServiceTypes_AreRegistered_MultipleTimes() + public void AddMvc_MultiRegistrationServiceTypes_AreRegistered_MultipleTimes() { // Arrange var services = new ServiceCollection(); services.AddSingleton(GetHostingEnvironment()); + RegisterMockMultiRegistrationServices(services); + // Act + services.AddMvc(); + + // Assert + VerifyMultiRegistrationServices(services); + } + + private void RegisterMockMultiRegistrationServices(IServiceCollection services) + { // Register a mock implementation of each service, AddMvcServices should add another implementation. foreach (var serviceType in MultiRegistrationServiceTypes) { var mockType = typeof(Mock<>).MakeGenericType(serviceType.Key); services.Add(ServiceDescriptor.Transient(serviceType.Key, mockType)); } + } - // Act - services.AddMvc(); - - // Assert + private void VerifyMultiRegistrationServices(IServiceCollection services) + { foreach (var serviceType in MultiRegistrationServiceTypes) { AssertServiceCountEquals(services, serviceType.Key, serviceType.Value.Length + 1); @@ -74,23 +79,78 @@ namespace Microsoft.AspNetCore.Mvc } [Fact] - public void SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes() + public void AddMvc_SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes() { // Arrange var services = new ServiceCollection(); services.AddSingleton(GetHostingEnvironment()); + RegisterMockSingleRegistrationServices(services); + // Act + services.AddMvc(); + + // Assert + VerifySingleRegistrationServices(services); + } + + [Fact] + public void AddControllers_AddRazorPages_SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + RegisterMockSingleRegistrationServices(services); + + // Act + services.AddControllers(); + services.AddRazorPages(); + + // Assert + VerifySingleRegistrationServices(services); + } + + [Fact] + public void AddControllersWithViews_SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + RegisterMockSingleRegistrationServices(services); + + // Act + services.AddControllers(); + + // Assert + VerifySingleRegistrationServices(services); + } + + [Fact] + public void AddRazorPages_SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + RegisterMockSingleRegistrationServices(services); + + // Act + services.AddRazorPages(); + + // Assert + VerifySingleRegistrationServices(services); + } + + private void RegisterMockSingleRegistrationServices(IServiceCollection services) + { // Register a mock implementation of each service, AddMvcServices should not replace it. foreach (var serviceType in SingleRegistrationServiceTypes) { var mockType = typeof(Mock<>).MakeGenericType(serviceType); services.Add(ServiceDescriptor.Transient(serviceType, mockType)); } + } - // Act - services.AddMvc(); - - // Assert + private void VerifySingleRegistrationServices(IServiceCollection services) + { foreach (var singleRegistrationType in SingleRegistrationServiceTypes) { AssertServiceCountEquals(services, singleRegistrationType, 1); @@ -98,7 +158,7 @@ namespace Microsoft.AspNetCore.Mvc } [Fact] - public void AddMvcServicesTwice_DoesNotAddDuplicates() + public void AddMvc_Twice_DoesNotAddDuplicates() { // Arrange var services = new ServiceCollection(); @@ -109,6 +169,58 @@ namespace Microsoft.AspNetCore.Mvc services.AddMvc(); // Assert + VerifyAllServices(services); + } + + [Fact] + public void AddControllersAddRazorPages_Twice_DoesNotAddDuplicates() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + + // Act + services.AddControllers(); + services.AddRazorPages(); + services.AddControllers(); + services.AddRazorPages(); + + // Assert + VerifyAllServices(services); + } + + [Fact] + public void AddControllersWithViews_Twice_DoesNotAddDuplicates() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + + // Act + services.AddControllersWithViews(); + services.AddControllersWithViews(); + + // Assert + VerifyAllServices(services); + } + + [Fact] + public void AddRazorPages_Twice_DoesNotAddDuplicates() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(GetHostingEnvironment()); + + // Act + services.AddRazorPages(); + services.AddRazorPages(); + + // Assert + VerifyAllServices(services); + } + + private void VerifyAllServices(IServiceCollection services) + { var singleRegistrationServiceTypes = SingleRegistrationServiceTypes; foreach (var service in services) { @@ -449,7 +561,8 @@ namespace Microsoft.AspNetCore.Mvc Assert.True( (expectedServiceRegistrationCount == actual), $"Expected service type '{serviceType}' to be registered {expectedServiceRegistrationCount}" + - $" time(s) but was actually registered {actual} time(s)."); + $" time(s) but was actually registered {actual} time(s)." + + string.Join(Environment.NewLine, serviceDescriptors.Select(sd => sd.ImplementationType))); } private void AssertContainsSingle( diff --git a/src/Mvc/test/WebSites/ApiExplorerWebSite/Startup.cs b/src/Mvc/test/WebSites/ApiExplorerWebSite/Startup.cs index ff736bf9c9..e810a1e666 100644 --- a/src/Mvc/test/WebSites/ApiExplorerWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/ApiExplorerWebSite/Startup.cs @@ -22,7 +22,7 @@ namespace ApiExplorerWebSite services.AddTransient(); var wellKnownChangeToken = new WellKnownChangeToken(); - services.AddMvc(options => + services.AddControllers(options => { options.Filters.AddService(typeof(ApiExplorerDataFilter)); diff --git a/src/Mvc/test/WebSites/ApplicationModelWebSite/Startup.cs b/src/Mvc/test/WebSites/ApplicationModelWebSite/Startup.cs index 7d288a4cf5..3c8dab4407 100644 --- a/src/Mvc/test/WebSites/ApplicationModelWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/ApplicationModelWebSite/Startup.cs @@ -14,7 +14,7 @@ namespace ApplicationModelWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc(options => + services.AddControllers(options => { options.Conventions.Add(new ApplicationDescription("Common Application Description")); options.Conventions.Add(new ControllerLicenseConvention()); @@ -23,6 +23,8 @@ namespace ApplicationModelWebSite options.Conventions.Add(new CloneActionConvention()); }) .SetCompatibilityVersion(CompatibilityVersion.Latest); + + services.AddRazorPages(); } public void Configure(IApplicationBuilder app) diff --git a/src/Mvc/test/WebSites/ControllersFromServicesWebSite/Startup.cs b/src/Mvc/test/WebSites/ControllersFromServicesWebSite/Startup.cs index 0202fc9f83..910cfd69aa 100644 --- a/src/Mvc/test/WebSites/ControllersFromServicesWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/ControllersFromServicesWebSite/Startup.cs @@ -22,7 +22,7 @@ namespace ControllersFromServicesWebSite public void ConfigureServices(IServiceCollection services) { var builder = services - .AddMvc() + .AddControllersWithViews() .ConfigureApplicationPartManager(manager => manager.ApplicationParts.Clear()) .AddApplicationPart(typeof(TimeScheduleController).GetTypeInfo().Assembly) .ConfigureApplicationPartManager(manager => diff --git a/src/Mvc/test/WebSites/CorsWebSite/Startup.cs b/src/Mvc/test/WebSites/CorsWebSite/Startup.cs index 9c68dc4d62..b8d4d7c727 100644 --- a/src/Mvc/test/WebSites/CorsWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/CorsWebSite/Startup.cs @@ -12,7 +12,7 @@ namespace CorsWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(ConfigureMvcOptions) + services.AddControllers(ConfigureMvcOptions) .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Latest); services.Configure(options => diff --git a/src/Mvc/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs b/src/Mvc/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs index c5e50c7a71..d8ba0f40d3 100644 --- a/src/Mvc/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/ErrorPageMiddlewareWebSite/Startup.cs @@ -14,7 +14,7 @@ namespace ErrorPageMiddlewareWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc() + services.AddControllersWithViews() .AddRazorRuntimeCompilation() .SetCompatibilityVersion(CompatibilityVersion.Latest); } diff --git a/src/Mvc/test/WebSites/FilesWebSite/Startup.cs b/src/Mvc/test/WebSites/FilesWebSite/Startup.cs index cd918afa87..518b69212f 100644 --- a/src/Mvc/test/WebSites/FilesWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/FilesWebSite/Startup.cs @@ -14,7 +14,7 @@ namespace FilesWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc() + services.AddControllers() .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Latest); } diff --git a/src/Mvc/test/WebSites/FormatterWebSite/Startup.cs b/src/Mvc/test/WebSites/FormatterWebSite/Startup.cs index 603e7dc618..ba036d9a08 100644 --- a/src/Mvc/test/WebSites/FormatterWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/FormatterWebSite/Startup.cs @@ -12,7 +12,7 @@ namespace FormatterWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(options => + services.AddControllers(options => { options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Developer))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Supplier))); diff --git a/src/Mvc/test/WebSites/FormatterWebSite/StartupWithRespectBrowserAcceptHeader.cs b/src/Mvc/test/WebSites/FormatterWebSite/StartupWithRespectBrowserAcceptHeader.cs index 9614f491f7..5459a54b36 100644 --- a/src/Mvc/test/WebSites/FormatterWebSite/StartupWithRespectBrowserAcceptHeader.cs +++ b/src/Mvc/test/WebSites/FormatterWebSite/StartupWithRespectBrowserAcceptHeader.cs @@ -10,7 +10,7 @@ namespace FormatterWebSite { public void ConfigureServices(IServiceCollection services) { - services.AddMvc(options => + services.AddControllers(options => { options.RespectBrowserAcceptHeader = true; }) diff --git a/src/Mvc/test/WebSites/GenericHostWebSite/Startup.cs b/src/Mvc/test/WebSites/GenericHostWebSite/Startup.cs index cb2ce81b4a..055adbe13b 100644 --- a/src/Mvc/test/WebSites/GenericHostWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/GenericHostWebSite/Startup.cs @@ -19,7 +19,7 @@ namespace GenericHostWebSite services.AddSingleton(new TestGenericService { Message = "true" }); services - .AddMvc(options => + .AddControllers(options => { // Remove when all URL generation tests are passing - https://github.com/aspnet/Routing/issues/590 options.EnableEndpointRouting = false; diff --git a/src/Mvc/test/WebSites/RazorPagesWebSite/Startup.cs b/src/Mvc/test/WebSites/RazorPagesWebSite/Startup.cs index f8bb11a78c..e75ac2f1ab 100644 --- a/src/Mvc/test/WebSites/RazorPagesWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/RazorPagesWebSite/Startup.cs @@ -15,8 +15,7 @@ namespace RazorPagesWebSite services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => options.LoginPath = "/Login"); - services.AddMvc() - .AddRazorPagesOptions(options => + services.AddRazorPages(options => { options.Conventions.AuthorizeFolder("/Admin"); }) diff --git a/src/Mvc/test/WebSites/SecurityWebSite/Startup.cs b/src/Mvc/test/WebSites/SecurityWebSite/Startup.cs index 0e26a546c2..93bae9b71f 100644 --- a/src/Mvc/test/WebSites/SecurityWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/SecurityWebSite/Startup.cs @@ -15,8 +15,7 @@ namespace SecurityWebSite public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc() - .SetCompatibilityVersion(CompatibilityVersion.Latest); + services.AddControllersWithViews().SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddAntiforgery(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => { diff --git a/src/Mvc/test/WebSites/TagHelpersWebSite/Startup.cs b/src/Mvc/test/WebSites/TagHelpersWebSite/Startup.cs index f01dac0414..f7caea4035 100644 --- a/src/Mvc/test/WebSites/TagHelpersWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/TagHelpersWebSite/Startup.cs @@ -14,7 +14,7 @@ namespace TagHelpersWebSite // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddMvc() + services.AddControllersWithViews() .SetCompatibilityVersion(CompatibilityVersion.Latest); } diff --git a/src/Mvc/test/WebSites/VersioningWebSite/Startup.cs b/src/Mvc/test/WebSites/VersioningWebSite/Startup.cs index 0314094df8..88510f4a9e 100644 --- a/src/Mvc/test/WebSites/VersioningWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/VersioningWebSite/Startup.cs @@ -12,8 +12,7 @@ namespace VersioningWebSite { public void ConfigureServices(IServiceCollection services) { - // Add MVC services to the services container - services.AddMvc(ConfigureMvcOptions) + services.AddControllers(ConfigureMvcOptions) .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Latest); diff --git a/src/Mvc/test/WebSites/XmlFormattersWebSite/Startup.cs b/src/Mvc/test/WebSites/XmlFormattersWebSite/Startup.cs index 0e45dec94a..10466f26c7 100644 --- a/src/Mvc/test/WebSites/XmlFormattersWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/XmlFormattersWebSite/Startup.cs @@ -19,7 +19,7 @@ namespace XmlFormattersWebSite public void ConfigureServices(IServiceCollection services) { // Add MVC services to the services container - services.AddMvc() + services.AddControllers() .AddXmlDataContractSerializerFormatters() .AddXmlSerializerFormatters() .SetCompatibilityVersion(CompatibilityVersion); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs index a250f47e74..9bf1044b02 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs @@ -20,7 +20,7 @@ namespace RazorComponentsWeb_CSharp // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - services.AddMvc() + services.AddRazorPages() .AddNewtonsoftJson(); services.AddRazorComponents(); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index 3f407be714..26d359acc7 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -119,7 +119,7 @@ namespace Company.WebApplication1 #endif #if (OrganizationalAuth) - services.AddMvc(options => + services.AddRazorPages().AddMvcOptions(options => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() @@ -128,7 +128,7 @@ namespace Company.WebApplication1 }) .AddNewtonsoftJson(); #else - services.AddMvc() + services.AddRazorPages() .AddNewtonsoftJson(); #endif } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index c6611407b5..5e1291823e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -118,7 +118,7 @@ namespace Company.WebApplication1 #endif #if (OrganizationalAuth) - services.AddMvc(options => + services.AddControllersWithViews(options => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() @@ -127,9 +127,10 @@ namespace Company.WebApplication1 }) .AddNewtonsoftJson(); #else - services.AddMvc() + services.AddControllersWithViews() .AddNewtonsoftJson(); #endif + services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs index fa65fda3f5..91d078759a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs @@ -22,7 +22,8 @@ type Startup private () = // This method gets called by the runtime. Use this method to add services to the container. member this.ConfigureServices(services: IServiceCollection) = // Add framework services. - services.AddMvc().AddNewtonsoftJson().AddRazorRuntimeCompilation() |> ignore + services.AddControllersWithViews().AddNewtonsoftJson().AddRazorRuntimeCompilation() |> ignore + services.AddRazorPages() |> ignore // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. member this.Configure(app: IApplicationBuilder, env: IWebHostEnvironment) = diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 987d2d282b..3088897131 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -44,7 +44,7 @@ namespace Company.WebApplication1 services.AddAuthentication(AzureADB2CDefaults.BearerAuthenticationScheme) .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options)); #endif - services.AddMvc() + services.AddControllers() .AddNewtonsoftJson(); } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs index 55ed03b223..3df5e59353 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs @@ -22,7 +22,7 @@ type Startup private () = // This method gets called by the runtime. Use this method to add services to the container. member this.ConfigureServices(services: IServiceCollection) = // Add framework services. - services.AddMvc().AddNewtonsoftJson() |> ignore + services.AddControllers().AddNewtonsoftJson() |> ignore // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. member this.Configure(app: IApplicationBuilder, env: IWebHostEnvironment) =