diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModelProviderContext.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModelProviderContext.cs new file mode 100644 index 0000000000..e6bb3fa760 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModelProviderContext.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc.ApplicationModels +{ + /// + /// A context object for . + /// + public class ApplicationModelProviderContext + { + public ApplicationModelProviderContext([NotNull] IEnumerable controllerTypes) + { + ControllerTypes = controllerTypes; + } + + public IEnumerable ControllerTypes { get; } + + /// + /// Gets the . + /// + public ApplicationModel Result { get; } = new ApplicationModel(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultActionModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultApplicationModelProvider.cs similarity index 58% rename from src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultActionModelBuilder.cs rename to src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultApplicationModelProvider.cs index b9c653eaea..81bd32780f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultActionModelBuilder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultApplicationModelProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -6,31 +6,262 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.AspNet.Authorization; -using Microsoft.AspNet.Cors; using Microsoft.AspNet.Cors.Core; using Microsoft.AspNet.Mvc.ApiExplorer; +using Microsoft.AspNet.Mvc.Filters; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.ModelBinding.Metadata; using Microsoft.AspNet.Mvc.Routing; using Microsoft.Framework.Internal; using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Mvc.ApplicationModels { - /// - /// A default implementation of . - /// - public class DefaultActionModelBuilder : IActionModelBuilder + public class DefaultApplicationModelProvider : IApplicationModelProvider { private readonly AuthorizationOptions _authorizationOptions; + private readonly ICollection _globalFilters; - public DefaultActionModelBuilder(IOptions authorizationOptions) + public DefaultApplicationModelProvider( + IOptions mvcOptionsAccessor, + IOptions authorizationOptionsAccessor) { - _authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions(); + _globalFilters = mvcOptionsAccessor.Options.Filters; + _authorizationOptions = authorizationOptionsAccessor.Options; } /// - public IEnumerable BuildActionModels([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo) + public int Order + { + get + { + return DefaultOrder.DefaultFrameworkSortOrder; + } + } + + /// + public virtual void OnProvidersExecuting([NotNull] ApplicationModelProviderContext context) + { + foreach (var filter in _globalFilters) + { + context.Result.Filters.Add(filter); + } + + foreach (var controllerType in context.ControllerTypes) + { + var controllerModels = BuildControllerModels(controllerType); + if (controllerModels != null) + { + foreach (var controllerModel in controllerModels) + { + context.Result.Controllers.Add(controllerModel); + controllerModel.Application = context.Result; + + foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType())) + { + var propertyInfo = propertyHelper.Property; + var propertyModel = CreatePropertyModel(propertyInfo); + if (propertyModel != null) + { + propertyModel.Controller = controllerModel; + controllerModel.ControllerProperties.Add(propertyModel); + } + } + + foreach (var methodInfo in controllerType.AsType().GetMethods()) + { + var actionModels = BuildActionModels(controllerType, methodInfo); + if (actionModels != null) + { + foreach (var actionModel in actionModels) + { + actionModel.Controller = controllerModel; + controllerModel.Actions.Add(actionModel); + + foreach (var parameterInfo in actionModel.ActionMethod.GetParameters()) + { + var parameterModel = CreateParameterModel(parameterInfo); + if (parameterModel != null) + { + parameterModel.Action = actionModel; + actionModel.Parameters.Add(parameterModel); + } + } + } + } + } + } + } + } + } + + /// + public virtual void OnProvidersExecuted([NotNull] ApplicationModelProviderContext context) + { + // Intentionally empty. + } + + /// + /// Creates the instances for the given controller . + /// + /// The controller . + /// + /// A set of instances for the given controller or + /// null if the does not represent a controller. + /// + protected virtual IEnumerable BuildControllerModels([NotNull] TypeInfo typeInfo) + { + var controllerModel = CreateControllerModel(typeInfo); + yield return controllerModel; + } + + /// + /// Creates a for the given . + /// + /// The . + /// A for the given . + protected virtual ControllerModel CreateControllerModel([NotNull] TypeInfo typeInfo) + { + // For attribute routes on a controller, we want want to support 'overriding' routes on a derived + // class. So we need to walk up the hierarchy looking for the first class to define routes. + // + // Then we want to 'filter' the set of attributes, so that only the effective routes apply. + var currentTypeInfo = typeInfo; + var objectTypeInfo = typeof(object).GetTypeInfo(); + + IRouteTemplateProvider[] routeAttributes = null; + + do + { + routeAttributes = currentTypeInfo + .GetCustomAttributes(inherit: false) + .OfType() + .ToArray(); + + if (routeAttributes.Length > 0) + { + // Found 1 or more route attributes. + break; + } + + currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo(); + } + while (currentTypeInfo != objectTypeInfo); + + // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType + // is needed to so that the result of ToArray() is object + var attributes = typeInfo.GetCustomAttributes(inherit: true).OfType().ToArray(); + + // This is fairly complicated so that we maintain referential equality between items in + // ControllerModel.Attributes and ControllerModel.Attributes[*].Attribute. + var filteredAttributes = new List(); + foreach (var attribute in attributes) + { + if (attribute is IRouteTemplateProvider) + { + // This attribute is a route-attribute, leave it out. + } + else + { + filteredAttributes.Add(attribute); + } + } + filteredAttributes.AddRange(routeAttributes); + + attributes = filteredAttributes.ToArray(); + + var controllerModel = new ControllerModel(typeInfo, attributes); + AddRange( + controllerModel.AttributeRoutes, routeAttributes.Select(a => new AttributeRouteModel(a))); + + controllerModel.ControllerName = + typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? + typeInfo.Name.Substring(0, typeInfo.Name.Length - "Controller".Length) : + typeInfo.Name; + + AddRange(controllerModel.ActionConstraints, attributes.OfType()); + AddRange(controllerModel.Filters, attributes.OfType()); + AddRange(controllerModel.RouteConstraints, attributes.OfType()); + + var enableCors = attributes.OfType().SingleOrDefault(); + if (enableCors != null) + { + controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName)); + } + + var disableCors = attributes.OfType().SingleOrDefault(); + if (disableCors != null) + { + controllerModel.Filters.Add(new DisableCorsAuthorizationFilter()); + } + + var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType()); + if (policy != null) + { + controllerModel.Filters.Add(new AuthorizeFilter(policy)); + } + + var apiVisibility = attributes.OfType().FirstOrDefault(); + if (apiVisibility != null) + { + controllerModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi; + } + + var apiGroupName = attributes.OfType().FirstOrDefault(); + if (apiGroupName != null) + { + controllerModel.ApiExplorer.GroupName = apiGroupName.GroupName; + } + + // Controllers can implement action filter and result filter interfaces. We add + // a special delegating filter implementation to the pipeline to handle it. + // + // This is needed because filters are instantiated before the controller. + if (typeof(IAsyncActionFilter).GetTypeInfo().IsAssignableFrom(typeInfo) || + typeof(IActionFilter).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + controllerModel.Filters.Add(new ControllerActionFilter()); + } + if (typeof(IAsyncResultFilter).GetTypeInfo().IsAssignableFrom(typeInfo) || + typeof(IResultFilter).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + controllerModel.Filters.Add(new ControllerResultFilter()); + } + + return controllerModel; + } + + /// + /// Creates a for the given . + /// + /// The . + /// A for the given . + protected virtual PropertyModel CreatePropertyModel([NotNull] PropertyInfo propertyInfo) + { + // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType + // is needed to so that the result of ToArray() is object + var attributes = propertyInfo.GetCustomAttributes(inherit: true).OfType().ToArray(); + var propertyModel = new PropertyModel(propertyInfo, attributes); + var bindingInfo = BindingInfo.GetBindingInfo(attributes); + + propertyModel.BindingInfo = bindingInfo; + propertyModel.PropertyName = propertyInfo.Name; + + return propertyModel; + } + + + /// + /// Creates the instances for the given action . + /// + /// The controller . + /// The action . + /// + /// A set of instances for the given action or + /// null if the does not represent an action. + /// + protected virtual IEnumerable BuildActionModels( + [NotNull] TypeInfo typeInfo, + [NotNull] MethodInfo methodInfo) { if (!IsAction(typeInfo, methodInfo)) { @@ -214,19 +445,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } } - foreach (var actionModel in actionModels) - { - foreach (var parameterInfo in actionModel.ActionMethod.GetParameters()) - { - var parameterModel = CreateParameterModel(parameterInfo); - if (parameterModel != null) - { - parameterModel.Action = actionModel; - actionModel.Parameters.Add(parameterModel); - } - } - } - return actionModels; } @@ -285,15 +503,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels return false; } - return - methodInfo.IsPublic; - } - - private bool IsIDisposableMethod(MethodInfo methodInfo, TypeInfo typeInfo) - { - return - (typeof(IDisposable).GetTypeInfo().IsAssignableFrom(typeInfo) && - typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0] == methodInfo); + return methodInfo.IsPublic; } /// @@ -402,6 +612,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels return parameterModel; } + private bool IsIDisposableMethod(MethodInfo methodInfo, TypeInfo typeInfo) + { + return + (typeof(IDisposable).GetTypeInfo().IsAssignableFrom(typeInfo) && + typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0] == methodInfo); + } + private bool IsSilentRouteAttribute(IRouteTemplateProvider routeTemplateProvider) { return diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs deleted file mode 100644 index 7a73d0d5c8..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.AspNet.Mvc.ApiExplorer; -using Microsoft.AspNet.Authorization; -using Microsoft.AspNet.Cors.Core; -using Microsoft.AspNet.Mvc.Filters; -using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.Routing; -using Microsoft.Framework.Internal; -using Microsoft.Framework.OptionsModel; - -namespace Microsoft.AspNet.Mvc.ApplicationModels -{ - /// - /// A default implementation of . - /// - public class DefaultControllerModelBuilder : IControllerModelBuilder - { - private readonly IActionModelBuilder _actionModelBuilder; - private readonly AuthorizationOptions _authorizationOptions; - - /// - /// Creates a new . - /// - /// The used to create actions. - public DefaultControllerModelBuilder( - IActionModelBuilder actionModelBuilder, - IOptions authorizationOptions) - { - _actionModelBuilder = actionModelBuilder; - _authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions(); - } - - /// - public ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo) - { - var controllerModel = CreateControllerModel(typeInfo); - var controllerType = typeInfo.AsType(); - - foreach (var methodInfo in controllerType.GetMethods()) - { - var actionModels = _actionModelBuilder.BuildActionModels(typeInfo, methodInfo); - if (actionModels != null) - { - foreach (var actionModel in actionModels) - { - actionModel.Controller = controllerModel; - controllerModel.Actions.Add(actionModel); - } - } - } - - foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType)) - { - var propertyInfo = propertyHelper.Property; - var propertyModel = CreatePropertyModel(propertyInfo); - if (propertyModel != null) - { - propertyModel.Controller = controllerModel; - controllerModel.ControllerProperties.Add(propertyModel); - } - } - - return controllerModel; - } - - /// - /// Creates an for the given . - /// - /// The . - /// A for the given . - protected virtual ControllerModel CreateControllerModel([NotNull] TypeInfo typeInfo) - { - // For attribute routes on a controller, we want want to support 'overriding' routes on a derived - // class. So we need to walk up the hierarchy looking for the first class to define routes. - // - // Then we want to 'filter' the set of attributes, so that only the effective routes apply. - var currentTypeInfo = typeInfo; - var objectTypeInfo = typeof(object).GetTypeInfo(); - - IRouteTemplateProvider[] routeAttributes = null; - - do - { - routeAttributes = currentTypeInfo - .GetCustomAttributes(inherit: false) - .OfType() - .ToArray(); - - if (routeAttributes.Length > 0) - { - // Found 1 or more route attributes. - break; - } - - currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo(); - } - while (currentTypeInfo != objectTypeInfo); - - // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType - // is needed to so that the result of ToArray() is object - var attributes = typeInfo.GetCustomAttributes(inherit: true).OfType().ToArray(); - - // This is fairly complicated so that we maintain referential equality between items in - // ControllerModel.Attributes and ControllerModel.Attributes[*].Attribute. - var filteredAttributes = new List(); - foreach (var attribute in attributes) - { - if (attribute is IRouteTemplateProvider) - { - // This attribute is a route-attribute, leave it out. - } - else - { - filteredAttributes.Add(attribute); - } - } - filteredAttributes.AddRange(routeAttributes); - - attributes = filteredAttributes.ToArray(); - - var controllerModel = new ControllerModel(typeInfo, attributes); - AddRange( - controllerModel.AttributeRoutes, routeAttributes.Select(a => new AttributeRouteModel(a))); - - controllerModel.ControllerName = - typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? - typeInfo.Name.Substring(0, typeInfo.Name.Length - "Controller".Length) : - typeInfo.Name; - - AddRange(controllerModel.ActionConstraints, attributes.OfType()); - AddRange(controllerModel.Filters, attributes.OfType()); - AddRange(controllerModel.RouteConstraints, attributes.OfType()); - - var enableCors = attributes.OfType().SingleOrDefault(); - if (enableCors != null) - { - controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName)); - } - - var disableCors = attributes.OfType().SingleOrDefault(); - if (disableCors != null) - { - controllerModel.Filters.Add(new DisableCorsAuthorizationFilter()); - } - - var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType()); - if (policy != null) - { - controllerModel.Filters.Add(new AuthorizeFilter(policy)); - } - - var apiVisibility = attributes.OfType().FirstOrDefault(); - if (apiVisibility != null) - { - controllerModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi; - } - - var apiGroupName = attributes.OfType().FirstOrDefault(); - if (apiGroupName != null) - { - controllerModel.ApiExplorer.GroupName = apiGroupName.GroupName; - } - - // Controllers can implement action filter and result filter interfaces. We add - // a special delegating filter implementation to the pipeline to handle it. - // - // This is needed because filters are instantiated before the controller. - if (typeof(IAsyncActionFilter).GetTypeInfo().IsAssignableFrom(typeInfo) || - typeof(IActionFilter).GetTypeInfo().IsAssignableFrom(typeInfo)) - { - controllerModel.Filters.Add(new ControllerActionFilter()); - } - if (typeof(IAsyncResultFilter).GetTypeInfo().IsAssignableFrom(typeInfo) || - typeof(IResultFilter).GetTypeInfo().IsAssignableFrom(typeInfo)) - { - controllerModel.Filters.Add(new ControllerResultFilter()); - } - - return controllerModel; - } - - /// - /// Creates a for the given . - /// - /// The . - /// A for the given . - protected virtual PropertyModel CreatePropertyModel([NotNull] PropertyInfo propertyInfo) - { - // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType - // is needed to so that the result of ToArray() is object - var attributes = propertyInfo.GetCustomAttributes(inherit: true).OfType().ToArray(); - var propertyModel = new PropertyModel(propertyInfo, attributes); - var bindingInfo = BindingInfo.GetBindingInfo(attributes); - - propertyModel.BindingInfo = bindingInfo; - propertyModel.PropertyName = propertyInfo.Name; - - return propertyModel; - } - - private static void AddRange(IList list, IEnumerable items) - { - foreach (var item in items) - { - list.Add(item); - } - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IActionModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IActionModelBuilder.cs deleted file mode 100644 index f9737b0e77..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IActionModelBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Reflection; -using Microsoft.Framework.Internal; - -namespace Microsoft.AspNet.Mvc.ApplicationModels -{ - /// - /// Creates a set of for a method. - /// - public interface IActionModelBuilder - { - /// - /// Creates a set of for a method. May return null or empty if - /// is not an action method. - /// - /// The . - /// The . - /// A set of or null. - /// - /// Instances of returned from this interface should have their - /// initialized. - /// - IEnumerable BuildActionModels([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo); - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApplicationModelProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApplicationModelProvider.cs new file mode 100644 index 0000000000..bd7d7a2201 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApplicationModelProvider.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc.ApplicationModels +{ + /// + /// Builds or modifies an for action discovery. + /// + public interface IApplicationModelProvider + { + /// + /// Gets the order value for determining the order of execution of providers. Providers execute in + /// ascending numeric value of the property. + /// + /// + /// + /// Providers are executed in an ordering determined by an ascending sort of the property. + /// A provider with a lower numeric value of will have its + /// called before that of a provider with a higher numeric value of + /// . The method is called in the reverse ordering after + /// all calls to . A provider with a lower numeric value of + /// will have its method called after that of a provider + /// with a higher numeric value of . + /// + /// + /// If two providers have the same numeric value of , then their relative execution order + /// is undefined. + /// + /// + int Order { get; } + + /// + /// Executed for the first pass of building. See . + /// + /// The . + void OnProvidersExecuting([NotNull] ApplicationModelProviderContext context); + + /// + /// Executed for the second pass of building. See . + /// + /// The . + void OnProvidersExecuted([NotNull] ApplicationModelProviderContext context); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IControllerModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IControllerModelBuilder.cs deleted file mode 100644 index 201f30a8bd..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IControllerModelBuilder.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Reflection; -using Microsoft.Framework.Internal; - -namespace Microsoft.AspNet.Mvc.ApplicationModels -{ - /// - /// Creates a set of for a type. - /// - public interface IControllerModelBuilder - { - /// - /// Creates a set of for a type. May return null or empty if - /// is not a controller type. - /// - /// The . - /// A or null. - /// - /// Instances of returned from this interface should have their - /// initialized. - /// - ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo); - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs index 789b224c2e..29aacd2bf2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; using Microsoft.AspNet.Mvc.ApplicationModels; -using Microsoft.AspNet.Mvc.Filters; using Microsoft.Framework.Internal; using Microsoft.Framework.OptionsModel; @@ -11,19 +11,17 @@ namespace Microsoft.AspNet.Mvc.Core { public class ControllerActionDescriptorProvider : IActionDescriptorProvider { - private readonly IControllerModelBuilder _applicationModelBuilder; + private readonly IApplicationModelProvider[] _applicationModelProviders; private readonly IControllerTypeProvider _controllerTypeProvider; - private readonly IReadOnlyList _globalFilters; private readonly IEnumerable _conventions; - public ControllerActionDescriptorProvider([NotNull] IControllerTypeProvider controllerTypeProvider, - [NotNull] IControllerModelBuilder applicationModelBuilder, - [NotNull] IGlobalFilterProvider globalFilters, - [NotNull] IOptions optionsAccessor) + public ControllerActionDescriptorProvider( + [NotNull] IControllerTypeProvider controllerTypeProvider, + [NotNull] IEnumerable applicationModelProviders, + [NotNull] IOptions optionsAccessor) { _controllerTypeProvider = controllerTypeProvider; - _applicationModelBuilder = applicationModelBuilder; - _globalFilters = globalFilters.Filters; + _applicationModelProviders = applicationModelProviders.OrderBy(p => p.Order).ToArray(); _conventions = optionsAccessor.Options.Conventions; } @@ -55,23 +53,20 @@ namespace Microsoft.AspNet.Mvc.Core internal protected ApplicationModel BuildModel() { - var applicationModel = new ApplicationModel(); - foreach (var filter in _globalFilters) + var controllerTypes = _controllerTypeProvider.ControllerTypes; + var context = new ApplicationModelProviderContext(controllerTypes); + + for (var i = 0; i < _applicationModelProviders.Length; i++) { - applicationModel.Filters.Add(filter); + _applicationModelProviders[i].OnProvidersExecuting(context); } - foreach (var type in _controllerTypeProvider.ControllerTypes) + for (var i = _applicationModelProviders.Length - 1 ; i >= 0; i--) { - var controllerModel = _applicationModelBuilder.BuildControllerModel(type); - if (controllerModel != null) - { - controllerModel.Application = applicationModel; - applicationModel.Controllers.Add(controllerModel); - } + _applicationModelProviders[i].OnProvidersExecuted(context); } - return applicationModel; + return context.Result; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultGlobalFilterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultGlobalFilterProvider.cs deleted file mode 100644 index 29aca07640..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultGlobalFilterProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.Framework.OptionsModel; - -namespace Microsoft.AspNet.Mvc.Filters -{ - /// - /// An implementation of based on . - /// - public class DefaultGlobalFilterProvider : IGlobalFilterProvider - { - private readonly IReadOnlyList _filters; - - /// - /// Creates a new instance of . - /// - /// The options accessor for . - public DefaultGlobalFilterProvider(IOptions optionsAccessor) - { - var filters = optionsAccessor.Options.Filters; - _filters = filters.ToList(); - } - - /// - public IReadOnlyList Filters - { - get - { - return _filters; - } - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/IGlobalFilterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/IGlobalFilterProvider.cs deleted file mode 100644 index 2ceb34b729..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/IGlobalFilterProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.AspNet.Mvc.Filters -{ - /// - /// Provides access to the collection of for globally registered filters. - /// - public interface IGlobalFilterProvider - { - /// - /// Gets the collection of . - /// - IReadOnlyList Filters { get; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs index edcf53cb40..1d1a60b254 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs @@ -116,10 +116,8 @@ namespace Microsoft.Framework.DependencyInjection // Core action discovery, filters and action execution. - // These are consumed only when creating action descriptors, then they can be de-allocated - services.TryAdd(ServiceDescriptor.Transient()); - services.TryAdd(ServiceDescriptor.Transient()); - services.TryAdd(ServiceDescriptor.Transient()); + // This are consumed only when creating action descriptors, then they can be de-allocated + services.TryAdd(ServiceDescriptor.Transient());; // This has a cache, so it needs to be a singleton services.TryAdd(ServiceDescriptor.Singleton()); @@ -155,12 +153,12 @@ namespace Microsoft.Framework.DependencyInjection services.TryAdd(ServiceDescriptor .Singleton()); - // The IGlobalFilterProvider is used to build the action descriptors (likely once) and so should - // remain transient to avoid keeping it in memory. - services.TryAdd(ServiceDescriptor.Transient()); // multiple registration service services.AddTransient(); + // multiple registration service + services.AddTransient(); + services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultActionModelBuilderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultApplicationModelProviderTest.cs similarity index 68% rename from test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultActionModelBuilderTest.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultApplicationModelProviderTest.cs index cb39b06884..b77f215a9e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultActionModelBuilderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultApplicationModelProviderTest.cs @@ -1,21 +1,190 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Cors.Core; +using Microsoft.AspNet.Mvc.Filters; +using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.Framework.Internal; using Microsoft.Framework.OptionsModel; -using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.ApplicationModels { - public class DefaultActionModelBuilderTest + public class DefaultApplicationModelProviderTest { + [Fact] + public void CreateControllerModel_DerivedFromControllerClass_HasFilter() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(StoreController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + var filter = Assert.Single(model.Filters); + Assert.IsType(filter); + } + + [Fact] + public void CreateControllerModel_AuthorizeAttributeAddsAuthorizeFilter() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(AccountController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.True(model.Filters.Any(f => f is AuthorizeFilter)); + } + + [Fact] + public void CreateControllerModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(CorsController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory); + } + + [Fact] + public void OnProvidersExecuting_AddsControllerProperties() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(ModelBinderController).GetTypeInfo(); + + var context = new ApplicationModelProviderContext(new[] { typeInfo }); + + // Act + builder.OnProvidersExecuting(context); + + // Assert + var model = Assert.Single(context.Result.Controllers); + Assert.Equal(2, model.ControllerProperties.Count); + Assert.Equal("Bound", model.ControllerProperties[0].PropertyName); + Assert.Equal(BindingSource.Query, model.ControllerProperties[0].BindingInfo.BindingSource); + Assert.NotNull(model.ControllerProperties[0].Controller); + var attribute = Assert.Single(model.ControllerProperties[0].Attributes); + Assert.IsType(attribute); + } + + [Fact] + public void CreateControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(DisableCorsController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.True(model.Filters.Any(f => f is DisableCorsAuthorizationFilter)); + } + + // This class has a filter attribute, but doesn't implement any filter interfaces, + // so ControllerFilter is not present. + [Fact] + public void CreateControllerModel_ClassWithoutFilterInterfaces_HasNoControllerFilter() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(NoFiltersController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + var filter = Assert.Single(model.Filters); + Assert.IsType(filter); + } + + [Fact] + public void CreateControllerModel_ClassWithFilterInterfaces_HasFilter() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(SomeFiltersController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.Single(model.Filters, f => f is ControllerActionFilter); + Assert.Single(model.Filters, f => f is ControllerResultFilter); + } + + [Fact] + public void CreateControllerModel_ClassWithFilterInterfaces_UnsupportedType() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(UnsupportedFiltersController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.Empty(model.Filters); + } + + [Fact] + public void CreateControllerModel_ClassWithInheritedRoutes() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(DerivedClassInheritingRoutesController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.Equal(2, model.AttributeRoutes.Count); + Assert.Equal(2, model.Attributes.Count); + + var route = Assert.Single(model.AttributeRoutes, r => r.Template == "A"); + Assert.Contains(route.Attribute, model.Attributes); + + route = Assert.Single(model.AttributeRoutes, r => r.Template == "B"); + Assert.Contains(route.Attribute, model.Attributes); + } + + [Fact] + public void CreateControllerModel_ClassWithHiddenInheritedRoutes() + { + // Arrange + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(DerivedClassHidingRoutesController).GetTypeInfo(); + + // Act + var model = builder.CreateControllerModel(typeInfo); + + // Assert + Assert.Equal(2, model.AttributeRoutes.Count); + Assert.Equal(2, model.Attributes.Count); + + var route = Assert.Single(model.AttributeRoutes, r => r.Template == "C"); + Assert.Contains(route.Attribute, model.Attributes); + + route = Assert.Single(model.AttributeRoutes, r => r.Template == "D"); + Assert.Contains(route.Attribute, model.Attributes); + } + [Theory] [InlineData("GetFromDerived", true)] [InlineData("NewMethod", true)] // "NewMethod" is a public method declared with keyword "new". @@ -23,7 +192,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_WithInheritedMethods(string methodName, bool expected) { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod(methodName); Assert.NotNull(method); @@ -38,7 +207,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_OverridenMethodControllerClass() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(BaseController).GetMethod(nameof(BaseController.Redirect)); Assert.NotNull(method); @@ -53,7 +222,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_PrivateMethod_FromUserDefinedController() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod( "PrivateMethod", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); @@ -70,7 +239,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_OperatorOverloadingMethod_FromOperatorOverloadingController() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(OperatorOverloadingController).GetMethod("op_Addition"); Assert.NotNull(method); Assert.True(method.IsSpecialName); @@ -86,7 +255,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_GenericMethod_FromUserDefinedController() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod("GenericMethod"); Assert.NotNull(method); @@ -101,7 +270,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_OverridenNonActionMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod("OverridenNonActionMethod"); Assert.NotNull(method); @@ -120,7 +289,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_OverriddenMethodsFromObjectClass(string methodName) { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod( methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); @@ -137,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_DerivedControllerIDisposableDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedController).GetTypeInfo(); var methodInfo = typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0]; @@ -155,7 +324,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_DerivedControllerDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedController).GetTypeInfo(); var methodInfo = typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0]; @@ -177,7 +346,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_OverriddenDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedOverriddenDisposeController).GetTypeInfo(); var method = typeInfo.GetDeclaredMethods("Dispose").SingleOrDefault(); Assert.NotNull(method); @@ -193,7 +362,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_NewDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedNewDisposeController).GetTypeInfo(); var method = typeInfo.GetDeclaredMethods("Dispose").SingleOrDefault(); Assert.NotNull(method); @@ -209,7 +378,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_PocoControllerIDisposableDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(IDisposablePocoController).GetTypeInfo(); var methodInfo = typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0]; @@ -227,7 +396,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_PocoControllerDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(IDisposablePocoController).GetTypeInfo(); var methodInfo = typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0]; @@ -249,7 +418,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_SimplePocoControllerDisposeMethod() { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(SimplePocoController).GetTypeInfo(); var methods = typeInfo.GetMethods().Where(m => m.Name.Equals("Dispose")); @@ -272,7 +441,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void IsAction_StaticMethods(string methodName) { // Arrange - var builder = CreateTestAccessibleActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var method = typeof(DerivedController).GetMethod( methodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); @@ -289,7 +458,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void BuildActionModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory() { // Arrange - var builder = new DefaultActionModelBuilder(authorizationOptions: null); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(EnableCorsController).GetTypeInfo(); var method = typeInfo.GetMethod("Action"); @@ -305,8 +474,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public void BuildActionModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter() { // Arrange - var builder = new DefaultActionModelBuilder(authorizationOptions: null); - var typeInfo = typeof(DisableCorsController).GetTypeInfo(); + var builder = new TestApplicationModelProvider(); + var typeInfo = typeof(DisableCorsActionController).GetTypeInfo(); var method = typeInfo.GetMethod("Action"); // Act @@ -318,10 +487,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_ConventionallyRoutedAction_WithoutHttpConstraints() + public void BuildActionModels_ConventionallyRoutedAction_WithoutHttpConstraints() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo(); var actionName = nameof(ConventionallyRoutedController.Edit); @@ -337,10 +506,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_ConventionallyRoutedAction_WithHttpConstraints() + public void BuildActionModels_ConventionallyRoutedAction_WithHttpConstraints() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo(); var actionName = nameof(ConventionallyRoutedController.Update); @@ -358,13 +527,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_BaseAuthorizeFiltersAreStillValidWhenOverriden() + public void BuildActionModels_BaseAuthorizeFiltersAreStillValidWhenOverriden() { // Arrange - var options = new AuthorizationOptions(); - options.AddPolicy("Base", policy => policy.RequireClaim("Basic").RequireClaim("Basic2")); - options.AddPolicy("Derived", policy => policy.RequireClaim("Derived")); - var builder = CreateTestDefaultActionModelBuilder(options); + var builder = new TestApplicationModelProvider(); + builder.AuthorizationOptions.AddPolicy("Base", policy => policy.RequireClaim("Basic").RequireClaim("Basic2")); + builder.AuthorizationOptions.AddPolicy("Derived", policy => policy.RequireClaim("Derived")); + var typeInfo = typeof(DerivedController).GetTypeInfo(); var actionName = nameof(DerivedController.Authorize); @@ -381,10 +550,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_ConventionallyRoutedActionWithHttpConstraints_AndInvalidRouteTemplateProvider() + public void BuildActionModels_ConventionallyRoutedActionWithHttpConstraints_AndInvalidRouteTemplateProvider() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo(); var actionName = nameof(ConventionallyRoutedController.Delete); @@ -403,10 +572,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_ConventionallyRoutedAction_WithMultipleHttpConstraints() + public void BuildActionModels_ConventionallyRoutedAction_WithMultipleHttpConstraints() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo(); var actionName = nameof(ConventionallyRoutedController.Details); @@ -422,10 +591,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_ConventionallyRoutedAction_WithMultipleOverlappingHttpConstraints() + public void BuildActionModels_ConventionallyRoutedAction_WithMultipleOverlappingHttpConstraints() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo(); var actionName = nameof(ConventionallyRoutedController.List); @@ -442,10 +611,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_AttributeRouteOnAction() + public void BuildActionModels_AttributeRouteOnAction() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo(); var actionName = nameof(NoRouteAttributeOnControllerController.Edit); @@ -467,10 +636,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_AttributeRouteOnAction_RouteAttribute() + public void BuildActionModels_AttributeRouteOnAction_RouteAttribute() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo(); var actionName = nameof(NoRouteAttributeOnControllerController.Update); @@ -491,10 +660,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_AttributeRouteOnAction_AcceptVerbsAttributeWithTemplate() + public void BuildActionModels_AttributeRouteOnAction_AcceptVerbsAttributeWithTemplate() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo(); var actionName = nameof(NoRouteAttributeOnControllerController.List); @@ -515,10 +684,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_AttributeRouteOnAction_CreatesOneActionInforPerRouteTemplate() + public void BuildActionModels_AttributeRouteOnAction_CreatesOneActionInforPerRouteTemplate() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo(); var actionName = nameof(NoRouteAttributeOnControllerController.Index); @@ -546,10 +715,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_NoRouteOnController_AllowsConventionallyRoutedActions_OnTheSameController() + public void BuildActionModels_NoRouteOnController_AllowsConventionallyRoutedActions_OnTheSameController() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo(); var actionName = nameof(NoRouteAttributeOnControllerController.Remove); @@ -571,10 +740,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels [Theory] [InlineData(typeof(SingleRouteAttributeController))] [InlineData(typeof(MultipleRouteAttributeController))] - public void GetActions_RouteAttributeOnController_CreatesAttributeRoute_ForNonAttributedActions(Type controller) + public void BuildActionModels_RouteAttributeOnController_CreatesAttributeRoute_ForNonAttributedActions(Type controller) { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = controller.GetTypeInfo(); // Act @@ -595,10 +764,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels [Theory] [InlineData(typeof(SingleRouteAttributeController))] [InlineData(typeof(MultipleRouteAttributeController))] - public void GetActions_RouteOnController_CreatesOneActionInforPerRouteTemplateOnAction(Type controller) + public void BuildActionModels_RouteOnController_CreatesOneActionInforPerRouteTemplateOnAction(Type controller) { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = controller.GetTypeInfo(); // Act @@ -624,10 +793,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_MixedHttpVerbsAndRoutes_EmptyVerbWithRoute() + public void BuildActionModels_MixedHttpVerbsAndRoutes_EmptyVerbWithRoute() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo(); var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.VerbAndRoute); @@ -641,10 +810,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyVerbsWithMultipleRoutes() + public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyVerbsWithMultipleRoutes() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo(); var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsAndRoutes); @@ -662,10 +831,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbsWithMultipleRoutes() + public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbsWithMultipleRoutes() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo(); var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsWithAnyWithoutTemplateAndRoutes); @@ -686,10 +855,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs() + public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo(); var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.Invalid); @@ -707,10 +876,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_InheritedAttributeRoutes() + public void BuildActionModels_InheritedAttributeRoutes() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedClassInheritsAttributeRoutesController).GetTypeInfo(); var actionName = nameof(DerivedClassInheritsAttributeRoutesController.Edit); @@ -730,10 +899,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } [Fact] - public void GetActions_InheritedAttributeRoutesOverridden() + public void BuildActionModels_InheritedAttributeRoutesOverridden() { // Arrange - var builder = CreateTestDefaultActionModelBuilder(); + var builder = new TestApplicationModelProvider(); var typeInfo = typeof(DerivedClassOverridesAttributeRoutesController).GetTypeInfo(); var actionName = nameof(DerivedClassOverridesAttributeRoutesController.Edit); @@ -752,31 +921,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels Assert.Contains(action.AttributeRouteModel.Attribute, action.Attributes); } - private static DefaultActionModelBuilder CreateTestDefaultActionModelBuilder( - AuthorizationOptions authOptions = null) - { - var options = new Mock>(); - options.Setup(o => o.Options).Returns(authOptions ?? new AuthorizationOptions()); - return new DefaultActionModelBuilder(options.Object); - } - - private static AccessibleActionModelBuilder CreateTestAccessibleActionModelBuilder() - { - var options = new Mock>(); - options.Setup(o => o.Options).Returns(new AuthorizationOptions()); - return new AccessibleActionModelBuilder(options.Object); - } - - private class AccessibleActionModelBuilder : DefaultActionModelBuilder - { - public AccessibleActionModelBuilder(IOptions options) : base(options) { } - - public new bool IsAction([NotNull] TypeInfo typeInfo, [NotNull]MethodInfo methodInfo) - { - return base.IsAction(typeInfo, methodInfo); - } - } - private class BaseClassWithAttributeRoutesController { [Route("A")] @@ -1039,7 +1183,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } } - private class DisableCorsController + private class DisableCorsActionController { [DisableCors] public void Action() @@ -1087,5 +1231,147 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels } } } + + [Route("A")] + [Route("B")] + private class BaseClassWithRoutesController + { + } + + private class DerivedClassInheritingRoutesController : BaseClassWithRoutesController + { + } + + [Route("C")] + [Route("D")] + private class DerivedClassHidingRoutesController : BaseClassWithRoutesController + { + } + + private class StoreController : Mvc.Controller + { + } + + [Produces("application/json")] + public class NoFiltersController + { + } + + [Authorize] + public class AccountController + { + } + + [EnableCors("policy")] + public class CorsController + { + } + + [DisableCors] + public class DisableCorsController + { + } + + public class ModelBinderController + { + [FromQuery] + public string Bound { get; set; } + + public string Unbound { get; set; } + } + + public class SomeFiltersController : IAsyncActionFilter, IResultFilter + { + public Task OnActionExecutionAsync( + [NotNull] ActionExecutingContext context, + [NotNull] ActionExecutionDelegate next) + { + return null; + } + + public void OnResultExecuted([NotNull] ResultExecutedContext context) + { + } + + public void OnResultExecuting([NotNull]ResultExecutingContext context) + { + } + } + + private class UnsupportedFiltersController : IExceptionFilter, IAuthorizationFilter, IAsyncResourceFilter + { + public void OnAuthorization([NotNull]AuthorizationContext context) + { + throw new NotImplementedException(); + } + + public void OnException([NotNull]ExceptionContext context) + { + throw new NotImplementedException(); + } + + public Task OnResourceExecutionAsync([NotNull]ResourceExecutingContext context, [NotNull]ResourceExecutionDelegate next) + { + throw new NotImplementedException(); + } + } + + private class TestApplicationModelProvider : DefaultApplicationModelProvider + { + public TestApplicationModelProvider() + : this( + new MockMvcOptionsAccessor(), + new OptionsManager(Enumerable.Empty>())) + { + } + + public TestApplicationModelProvider( + IOptions options, + IOptions authorizationOptions) + : base(options, authorizationOptions) + { + Options = options.Options; + AuthorizationOptions = authorizationOptions.Options; + } + + public AuthorizationOptions AuthorizationOptions { get; } + + public MvcOptions Options { get; } + + public new IEnumerable BuildControllerModels(TypeInfo typeInfo) + { + return base.BuildControllerModels(typeInfo); + } + + public new ControllerModel CreateControllerModel(TypeInfo typeInfo) + { + return base.CreateControllerModel(typeInfo); + } + + public new PropertyModel CreatePropertyModel(PropertyInfo propertyInfo) + { + return base.CreatePropertyModel(propertyInfo); + } + + public new IEnumerable BuildActionModels(TypeInfo typeInfo, MethodInfo methodInfo) + { + return base.BuildActionModels(typeInfo, methodInfo); + } + + public new ActionModel CreateActionModel(MethodInfo methodInfo, IReadOnlyList attributes) + { + return base.CreateActionModel(methodInfo, attributes); + } + + public new bool IsAction(TypeInfo typeInfo, MethodInfo methodInfo) + { + return base.IsAction(typeInfo, methodInfo); + } + + public new ParameterModel CreateParameterModel(ParameterInfo parameterInfo) + { + return base.CreateParameterModel(parameterInfo); + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs deleted file mode 100644 index 4166aba409..0000000000 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNet.Authorization; -using Microsoft.AspNet.Cors.Core; -using Microsoft.AspNet.Mvc.Filters; -using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.Framework.Internal; -using Microsoft.Framework.OptionsModel; -using Moq; -using Xunit; - -namespace Microsoft.AspNet.Mvc.ApplicationModels -{ - public class DefaultControllerModelBuilderTest - { - [Fact] - public void BuildControllerModel_DerivedFromControllerClass_HasFilter() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(StoreController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - var filter = Assert.Single(model.Filters); - Assert.IsType(filter); - } - - [Fact] - public void BuildControllerModel_AuthorizeAttributeAddsAuthorizeFilter() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(AccountController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.True(model.Filters.Any(f => f is AuthorizeFilter)); - } - - [Fact] - public void BuildControllerModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory() - { - // Arrange - var corsOptions = new CorsOptions(); - corsOptions.AddPolicy("policy", new CorsPolicy()); - var mockOptions = new Mock>(); - mockOptions.SetupGet(o => o.Options) - .Returns(corsOptions); - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), - authorizationOptions: null); - var typeInfo = typeof(CorsController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory); - } - - [Fact] - public void BuildControllerModel_AddsControllerProperties() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(ModelBinderController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Equal(2, model.ControllerProperties.Count); - Assert.Equal("Bound", model.ControllerProperties[0].PropertyName); - Assert.Equal(BindingSource.Query, model.ControllerProperties[0].BindingInfo.BindingSource); - Assert.NotNull(model.ControllerProperties[0].Controller); - var attribute = Assert.Single(model.ControllerProperties[0].Attributes); - Assert.IsType(attribute); - } - - [Fact] - public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter() - { - // Arrange - var corsOptions = new CorsOptions(); - corsOptions.AddPolicy("policy", new CorsPolicy()); - var mockOptions = new Mock>(); - mockOptions.SetupGet(o => o.Options) - .Returns(corsOptions); - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), - authorizationOptions: null); - var typeInfo = typeof(DisableCorsController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.True(model.Filters.Any(f => f is DisableCorsAuthorizationFilter)); - } - - // This class has a filter attribute, but doesn't implement any filter interfaces, - // so ControllerFilter is not present. - [Fact] - public void BuildControllerModel_ClassWithoutFilterInterfaces_HasNoControllerFilter() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(NoFiltersController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - var filter = Assert.Single(model.Filters); - Assert.IsType(filter); - } - - [Fact] - public void BuildControllerModel_ClassWithFilterInterfaces_HasFilter() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(SomeFiltersController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Single(model.Filters, f => f is ControllerActionFilter); - Assert.Single(model.Filters, f => f is ControllerResultFilter); - } - - [Fact] - public void BuildControllerModel_ClassWithFilterInterfaces_UnsupportedType() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(UnsupportedFiltersController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Empty(model.Filters); - } - - [Fact] - public void BuildControllerModel_ClassWithInheritedRoutes() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(DerivedClassInheritingRoutesController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Equal(2, model.AttributeRoutes.Count); - Assert.Equal(2, model.Attributes.Count); - - var route = Assert.Single(model.AttributeRoutes, r => r.Template == "A"); - Assert.Contains(route.Attribute, model.Attributes); - - route = Assert.Single(model.AttributeRoutes, r => r.Template == "B"); - Assert.Contains(route.Attribute, model.Attributes); - } - - [Fact] - public void BuildControllerModel_ClassWithHiddenInheritedRoutes() - { - // Arrange - var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var typeInfo = typeof(DerivedClassHidingRoutesController).GetTypeInfo(); - - // Act - var model = builder.BuildControllerModel(typeInfo); - - // Assert - Assert.Equal(2, model.AttributeRoutes.Count); - Assert.Equal(2, model.Attributes.Count); - - var route = Assert.Single(model.AttributeRoutes, r => r.Template == "C"); - Assert.Contains(route.Attribute, model.Attributes); - - route = Assert.Single(model.AttributeRoutes, r => r.Template == "D"); - Assert.Contains(route.Attribute, model.Attributes); - } - - [Route("A")] - [Route("B")] - private class BaseClassWithRoutesController - { - } - - private class DerivedClassInheritingRoutesController : BaseClassWithRoutesController - { - } - - [Route("C")] - [Route("D")] - private class DerivedClassHidingRoutesController : BaseClassWithRoutesController - { - } - - private class StoreController : Mvc.Controller - { - } - - [Produces("application/json")] - public class NoFiltersController - { - } - - [Authorize] - public class AccountController - { - } - - [EnableCors("policy")] - public class CorsController - { - } - - [DisableCors] - public class DisableCorsController - { - } - - public class ModelBinderController - { - [FromQuery] - public string Bound { get; set; } - - public string Unbound { get; set; } - } - - public class SomeFiltersController : IAsyncActionFilter, IResultFilter - { - public Task OnActionExecutionAsync( - [NotNull] ActionExecutingContext context, - [NotNull] ActionExecutionDelegate next) - { - return null; - } - - public void OnResultExecuted([NotNull] ResultExecutedContext context) - { - } - - public void OnResultExecuting([NotNull]ResultExecutingContext context) - { - } - } - - private class UnsupportedFiltersController : IExceptionFilter, IAuthorizationFilter, IAsyncResourceFilter - { - public void OnAuthorization([NotNull]AuthorizationContext context) - { - throw new NotImplementedException(); - } - - public void OnException([NotNull]ExceptionContext context) - { - throw new NotImplementedException(); - } - - public Task OnResourceExecutionAsync([NotNull]ResourceExecutingContext context, [NotNull]ResourceExecutionDelegate next) - { - throw new NotImplementedException(); - } - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs index f9f6cdf502..4515f0ef7e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Mvc.ApiExplorer; using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.AspNet.Mvc.Core; @@ -1449,61 +1450,63 @@ namespace Microsoft.AspNet.Mvc.Test TypeInfo controllerTypeInfo, IEnumerable filters = null) { + var options = new MockMvcOptionsAccessor(); + if (filters != null) + { + foreach (var filter in filters) + { + options.Options.Filters.Add(filter); + } + } + var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { controllerTypeInfo }); - var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); + var modelProvider = new DefaultApplicationModelProvider(options, new MockAuthorizationOptionsAccessor()); var provider = new ControllerActionDescriptorProvider( controllerTypeProvider, - controllerModelBuilder, - new TestGlobalFilterProvider(filters), - new MockMvcOptionsAccessor()); + new[] { modelProvider }, + options); return provider; } private ControllerActionDescriptorProvider GetProvider( - params TypeInfo[] controllerTypeInfo) + params TypeInfo[] controllerTypeInfos) { - var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfo); - var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); + var options = new MockMvcOptionsAccessor(); + + var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfos); + var modelProvider = new DefaultApplicationModelProvider(options, new MockAuthorizationOptionsAccessor()); var provider = new ControllerActionDescriptorProvider( controllerTypeProvider, - controllerModelBuilder, - new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); + new[] { modelProvider }, + options); return provider; } private ControllerActionDescriptorProvider GetProvider( - TypeInfo type, + TypeInfo controllerTypeInfo, IApplicationModelConvention convention) { - var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { type }); - var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - var options = new MockMvcOptionsAccessor(); options.Options.Conventions.Add(convention); - return new ControllerActionDescriptorProvider( + var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { controllerTypeInfo }); + var modelProvider = new DefaultApplicationModelProvider(options, new MockAuthorizationOptionsAccessor()); + + var provider = new ControllerActionDescriptorProvider( controllerTypeProvider, - modelBuilder, - new TestGlobalFilterProvider(), + new[] { modelProvider }, options); + + return provider; } private IEnumerable GetDescriptors(params TypeInfo[] controllerTypeInfos) { - var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfos); - var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - - var provider = new ControllerActionDescriptorProvider( - controllerTypeProvider, - modelBuilder, - new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); - + var provider = GetProvider(controllerTypeInfos); return provider.GetDescriptors(); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs index ec737dd61f..f428de3188 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs @@ -7,6 +7,7 @@ using System.ComponentModel.Design; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Mvc.ActionConstraints; @@ -667,14 +668,17 @@ namespace Microsoft.AspNet.Mvc .Select(t => t.GetTypeInfo()) .ToList(); - var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypes); - var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); + var options = new MockMvcOptionsAccessor(); - return new ControllerActionDescriptorProvider( - controllerTypeProvider, - modelBuilder, - new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); + var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypes); + var modelProvider = new DefaultApplicationModelProvider(options, new MockAuthorizationOptionsAccessor()); + + var provider = new ControllerActionDescriptorProvider( + controllerTypeProvider, + new[] { modelProvider }, + options); + + return provider; } private static HttpContext GetHttpContext(string httpMethod) diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/MockAuthorizationOptionsAccessor.cs b/test/Microsoft.AspNet.Mvc.Core.Test/MockAuthorizationOptionsAccessor.cs new file mode 100644 index 0000000000..b17c0902a5 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/MockAuthorizationOptionsAccessor.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.OptionsModel; + +namespace Microsoft.AspNet.Authorization +{ + public class MockAuthorizationOptionsAccessor : IOptions + { + public AuthorizationOptions Options { get; } = new AuthorizationOptions(); + + public AuthorizationOptions GetNamedOptions(string name) + { + return Options; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/TestGlobalFilterProvider.cs b/test/Microsoft.AspNet.Mvc.Core.Test/TestGlobalFilterProvider.cs deleted file mode 100644 index fb18e50b8b..0000000000 --- a/test/Microsoft.AspNet.Mvc.Core.Test/TestGlobalFilterProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using Microsoft.AspNet.Mvc.Filters; - -namespace Microsoft.AspNet.Mvc -{ - public class TestGlobalFilterProvider : IGlobalFilterProvider - { - public TestGlobalFilterProvider() - : this(null) - { - } - - public TestGlobalFilterProvider(IEnumerable filters) - { - var filterList = new List(); - Filters = filterList; - - if (filters != null) - { - filterList.AddRange(filters); - } - } - - public IReadOnlyList Filters { get; private set; } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs index 8772d6a874..68b6ca879d 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using Microsoft.AspNet.Mvc.ActionConstraints; using Microsoft.AspNet.Mvc.ApiExplorer; +using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.MvcServiceCollectionExtensionsTestControllers; using Microsoft.AspNet.Mvc.Razor; @@ -140,7 +141,8 @@ namespace Microsoft.AspNet.Mvc typeof(IActionDescriptorProvider), typeof(IActionInvokerProvider), typeof(IFilterProvider), - typeof(IApiDescriptionProvider) + typeof(IApiDescriptionProvider), + typeof(IApplicationModelProvider), }; } } diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs index 86aa5e69c8..7e38e9a8e4 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.AspNet.Mvc.Core; @@ -372,12 +373,6 @@ namespace System.Web.Http var assemblyProvider = new FixedSetAssemblyProvider(); assemblyProvider.CandidateAssemblies.Add(GetType().GetTypeInfo().Assembly); var controllerTypeProvider = new NamespaceFilteredControllerTypeProvider(assemblyProvider); - var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null), null); - - var filterProvider = new Mock(); - filterProvider - .SetupGet(fp => fp.Filters) - .Returns(new List()); var options = new MvcOptions(); @@ -389,10 +384,18 @@ namespace System.Web.Http .SetupGet(o => o.Options) .Returns(options); + var authorizationOptionsAccessor = new Mock>(); + authorizationOptionsAccessor + .SetupGet(o => o.Options) + .Returns(new AuthorizationOptions()); + + var modelProvider = new DefaultApplicationModelProvider( + optionsAccessor.Object, + authorizationOptionsAccessor.Object); + var provider = new ControllerActionDescriptorProvider( controllerTypeProvider, - modelBuilder, - filterProvider.Object, + new[] { modelProvider }, optionsAccessor.Object); return provider;