Refactor I***ModelBuilder extensibility

Combining IControllerModelBuilder and IActionModelBuilder into a pipeline
of IApplicationModelBuilders. Extensibility for framework features (Auth,
Cors, etc) should implement an IApplicationModelBuilder to add data to
models before IApplicationModelConventions have a chance to run.

Also deleting IGlobalFilterProvider while touching this code, this should
have been removed a while ago when we removed other options facades.
This commit is contained in:
Ryan Nowak 2015-06-05 16:38:29 -07:00
parent 261975b0bc
commit 6cd277e8aa
18 changed files with 787 additions and 822 deletions

View File

@ -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
{
/// <summary>
/// A context object for <see cref="IApplicationModelProvider"/>.
/// </summary>
public class ApplicationModelProviderContext
{
public ApplicationModelProviderContext([NotNull] IEnumerable<TypeInfo> controllerTypes)
{
ControllerTypes = controllerTypes;
}
public IEnumerable<TypeInfo> ControllerTypes { get; }
/// <summary>
/// Gets the <see cref="ApplicationModel"/>.
/// </summary>
public ApplicationModel Result { get; } = new ApplicationModel();
}
}

View File

@ -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
{
/// <summary>
/// A default implementation of <see cref="IActionModelBuilder"/>.
/// </summary>
public class DefaultActionModelBuilder : IActionModelBuilder
public class DefaultApplicationModelProvider : IApplicationModelProvider
{
private readonly AuthorizationOptions _authorizationOptions;
private readonly ICollection<IFilter> _globalFilters;
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> authorizationOptions)
public DefaultApplicationModelProvider(
IOptions<MvcOptions> mvcOptionsAccessor,
IOptions<AuthorizationOptions> authorizationOptionsAccessor)
{
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
_globalFilters = mvcOptionsAccessor.Options.Filters;
_authorizationOptions = authorizationOptionsAccessor.Options;
}
/// <inheritdoc />
public IEnumerable<ActionModel> BuildActionModels([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo)
public int Order
{
get
{
return DefaultOrder.DefaultFrameworkSortOrder;
}
}
/// <inheritdoc />
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);
}
}
}
}
}
}
}
}
}
/// <inheritdoc />
public virtual void OnProvidersExecuted([NotNull] ApplicationModelProviderContext context)
{
// Intentionally empty.
}
/// <summary>
/// Creates the <see cref="ControllerModel"/> instances for the given controller <see cref="TypeInfo"/>.
/// </summary>
/// <param name="typeInfo">The controller <see cref="TypeInfo"/>.</param>
/// <returns>
/// A set of <see cref="ControllerModel"/> instances for the given controller <see cref="TypeInfo"/> or
/// <c>null</c> if the <paramref name="typeInfo"/> does not represent a controller.
/// </returns>
protected virtual IEnumerable<ControllerModel> BuildControllerModels([NotNull] TypeInfo typeInfo)
{
var controllerModel = CreateControllerModel(typeInfo);
yield return controllerModel;
}
/// <summary>
/// Creates a <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>A <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.</returns>
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<IRouteTemplateProvider>()
.ToArray();
if (routeAttributes.Length > 0)
{
// Found 1 or more route attributes.
break;
}
currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo();
}
while (currentTypeInfo != objectTypeInfo);
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = typeInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
// This is fairly complicated so that we maintain referential equality between items in
// ControllerModel.Attributes and ControllerModel.Attributes[*].Attribute.
var filteredAttributes = new List<object>();
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<IActionConstraintMetadata>());
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
if (enableCors != null)
{
controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
}
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
if (disableCors != null)
{
controllerModel.Filters.Add(new DisableCorsAuthorizationFilter());
}
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{
controllerModel.Filters.Add(new AuthorizeFilter(policy));
}
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
if (apiVisibility != null)
{
controllerModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
}
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().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;
}
/// <summary>
/// Creates a <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.
/// </summary>
/// <param name="propertyInfo">The <see cref="PropertyInfo"/>.</param>
/// <returns>A <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.</returns>
protected virtual PropertyModel CreatePropertyModel([NotNull] PropertyInfo propertyInfo)
{
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = propertyInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
var propertyModel = new PropertyModel(propertyInfo, attributes);
var bindingInfo = BindingInfo.GetBindingInfo(attributes);
propertyModel.BindingInfo = bindingInfo;
propertyModel.PropertyName = propertyInfo.Name;
return propertyModel;
}
/// <summary>
/// Creates the <see cref="ControllerModel"/> instances for the given action <see cref="MethodInfo"/>.
/// </summary>
/// <param name="typeInfo">The controller <see cref="TypeInfo"/>.</param>
/// <param name="methodInfo">The action <see cref="MethodInfo"/>.</param>
/// <returns>
/// A set of <see cref="ActionModel"/> instances for the given action <see cref="MethodInfo"/> or
/// <c>null</c> if the <paramref name="methodInfo"/> does not represent an action.
/// </returns>
protected virtual IEnumerable<ActionModel> 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;
}
/// <summary>
@ -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

View File

@ -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
{
/// <summary>
/// A default implementation of <see cref="IControllerModelBuilder"/>.
/// </summary>
public class DefaultControllerModelBuilder : IControllerModelBuilder
{
private readonly IActionModelBuilder _actionModelBuilder;
private readonly AuthorizationOptions _authorizationOptions;
/// <summary>
/// Creates a new <see cref="DefaultControllerModelBuilder"/>.
/// </summary>
/// <param name="actionModelBuilder">The <see cref="IActionModelBuilder"/> used to create actions.</param>
public DefaultControllerModelBuilder(
IActionModelBuilder actionModelBuilder,
IOptions<AuthorizationOptions> authorizationOptions)
{
_actionModelBuilder = actionModelBuilder;
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
}
/// <inheritdoc />
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;
}
/// <summary>
/// Creates an <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>A <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.</returns>
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<IRouteTemplateProvider>()
.ToArray();
if (routeAttributes.Length > 0)
{
// Found 1 or more route attributes.
break;
}
currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo();
}
while (currentTypeInfo != objectTypeInfo);
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = typeInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
// This is fairly complicated so that we maintain referential equality between items in
// ControllerModel.Attributes and ControllerModel.Attributes[*].Attribute.
var filteredAttributes = new List<object>();
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<IActionConstraintMetadata>());
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
if (enableCors != null)
{
controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
}
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
if (disableCors != null)
{
controllerModel.Filters.Add(new DisableCorsAuthorizationFilter());
}
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{
controllerModel.Filters.Add(new AuthorizeFilter(policy));
}
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
if (apiVisibility != null)
{
controllerModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
}
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().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;
}
/// <summary>
/// Creates a <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.
/// </summary>
/// <param name="propertyInfo">The <see cref="PropertyInfo"/>.</param>
/// <returns>A <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.</returns>
protected virtual PropertyModel CreatePropertyModel([NotNull] PropertyInfo propertyInfo)
{
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = propertyInfo.GetCustomAttributes(inherit: true).OfType<object>().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<T>(IList<T> list, IEnumerable<T> items)
{
foreach (var item in items)
{
list.Add(item);
}
}
}
}

View File

@ -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
{
/// <summary>
/// Creates a set of <see cref="ActionModel"/> for a method.
/// </summary>
public interface IActionModelBuilder
{
/// <summary>
/// Creates a set of <see cref="ActionModel"/> for a method. May return null or empty if
/// <paramref name="methodInfo"/> is not an action method.
/// </summary>
/// <param name="methodInfo">The <see cref="MethodInfo"/>.</param>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>A set of <see cref="ActionModel"/> or null.</returns>
/// <remarks>
/// Instances of <see cref="ActionModel"/> returned from this interface should have their
/// <see cref="ActionModel.Parameters"/> initialized.
/// </remarks>
IEnumerable<ActionModel> BuildActionModels([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo);
}
}

View File

@ -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
{
/// <summary>
/// Builds or modifies an <see cref="ApplicationModel"/> for action discovery.
/// </summary>
public interface IApplicationModelProvider
{
/// <summary>
/// Gets the order value for determining the order of execution of providers. Providers execute in
/// ascending numeric value of the <see cref="Order"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Providers are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
/// A provider with a lower numeric value of <see cref="Order"/> will have its
/// <see cref="OnProvidersExecuting"/> called before that of a provider with a higher numeric value of
/// <see cref="Order"/>. The <see cref="OnProvidersExecuted"/> method is called in the reverse ordering after
/// all calls to <see cref="OnProvidersExecuting"/>. A provider with a lower numeric value of
/// <see cref="Order"/> will have its <see cref="OnProvidersExecuted"/> method called after that of a provider
/// with a higher numeric value of <see cref="Order"/>.
/// </para>
/// <para>
/// If two providers have the same numeric value of <see cref="Order"/>, then their relative execution order
/// is undefined.
/// </para>
/// </remarks>
int Order { get; }
/// <summary>
/// Executed for the first pass of <see cref="ApplicationModel"/> building. See <see cref="Order"/>.
/// </summary>
/// <param name="context">The <see cref="ApplicationModelProviderContext"/>.</param>
void OnProvidersExecuting([NotNull] ApplicationModelProviderContext context);
/// <summary>
/// Executed for the second pass of <see cref="ApplicationModel"/> building. See <see cref="Order"/>.
/// </summary>
/// <param name="context">The <see cref="ApplicationModelProviderContext"/>.</param>
void OnProvidersExecuted([NotNull] ApplicationModelProviderContext context);
}
}

View File

@ -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
{
/// <summary>
/// Creates a set of <see cref="ControllerModel"/> for a type.
/// </summary>
public interface IControllerModelBuilder
{
/// <summary>
/// Creates a set of <see cref="ControllerModel"/> for a type. May return null or empty if
/// <paramref name="typeInfo"/> is not a controller type.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>A <see cref="ControllerModel"/> or null.</returns>
/// <remarks>
/// Instances of <see cref="ControllerModel"/> returned from this interface should have their
/// <see cref="ControllerModel.Actions"/> initialized.
/// </remarks>
ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo);
}
}

View File

@ -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<IFilter> _globalFilters;
private readonly IEnumerable<IApplicationModelConvention> _conventions;
public ControllerActionDescriptorProvider([NotNull] IControllerTypeProvider controllerTypeProvider,
[NotNull] IControllerModelBuilder applicationModelBuilder,
[NotNull] IGlobalFilterProvider globalFilters,
[NotNull] IOptions<MvcOptions> optionsAccessor)
public ControllerActionDescriptorProvider(
[NotNull] IControllerTypeProvider controllerTypeProvider,
[NotNull] IEnumerable<IApplicationModelProvider> applicationModelProviders,
[NotNull] IOptions<MvcOptions> 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;
}
}
}

View File

@ -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
{
/// <summary>
/// An implementation of <see cref="IGlobalFilterProvider"/> based on <see cref="MvcOptions"/>.
/// </summary>
public class DefaultGlobalFilterProvider : IGlobalFilterProvider
{
private readonly IReadOnlyList<IFilter> _filters;
/// <summary>
/// Creates a new instance of <see cref="DefaultGlobalFilterProvider"/>.
/// </summary>
/// <param name="optionsAccessor">The options accessor for <see cref="MvcOptions"/>.</param>
public DefaultGlobalFilterProvider(IOptions<MvcOptions> optionsAccessor)
{
var filters = optionsAccessor.Options.Filters;
_filters = filters.ToList();
}
/// <inheritdoc />
public IReadOnlyList<IFilter> Filters
{
get
{
return _filters;
}
}
}
}

View File

@ -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
{
/// <summary>
/// Provides access to the collection of <see cref="IFilter"/> for globally registered filters.
/// </summary>
public interface IGlobalFilterProvider
{
/// <summary>
/// Gets the collection of <see cref="IFilter"/>.
/// </summary>
IReadOnlyList<IFilter> Filters { get; }
}
}

View File

@ -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<IControllerTypeProvider, DefaultControllerTypeProvider>());
services.TryAdd(ServiceDescriptor.Transient<IControllerModelBuilder, DefaultControllerModelBuilder>());
services.TryAdd(ServiceDescriptor.Transient<IActionModelBuilder, DefaultActionModelBuilder>());
// This are consumed only when creating action descriptors, then they can be de-allocated
services.TryAdd(ServiceDescriptor.Transient<IControllerTypeProvider, DefaultControllerTypeProvider>());;
// This has a cache, so it needs to be a singleton
services.TryAdd(ServiceDescriptor.Singleton<IControllerFactory, DefaultControllerFactory>());
@ -155,12 +153,12 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor
.Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>());
// 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<IGlobalFilterProvider, DefaultGlobalFilterProvider>());
// multiple registration service
services.AddTransient<IFilterProvider, DefaultFilterProvider>();
// multiple registration service
services.AddTransient<IApplicationModelProvider, DefaultApplicationModelProvider>();
services.TryAdd(ServiceDescriptor.Transient<FormatFilter, FormatFilter>());
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());

View File

@ -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<ControllerActionFilter>(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<FromQueryAttribute>(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<ProducesAttribute>(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<IOptions<AuthorizationOptions>>();
options.Setup(o => o.Options).Returns(authOptions ?? new AuthorizationOptions());
return new DefaultActionModelBuilder(options.Object);
}
private static AccessibleActionModelBuilder CreateTestAccessibleActionModelBuilder()
{
var options = new Mock<IOptions<AuthorizationOptions>>();
options.Setup(o => o.Options).Returns(new AuthorizationOptions());
return new AccessibleActionModelBuilder(options.Object);
}
private class AccessibleActionModelBuilder : DefaultActionModelBuilder
{
public AccessibleActionModelBuilder(IOptions<AuthorizationOptions> 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<AuthorizationOptions>(Enumerable.Empty<IConfigureOptions<AuthorizationOptions>>()))
{
}
public TestApplicationModelProvider(
IOptions<MvcOptions> options,
IOptions<AuthorizationOptions> authorizationOptions)
: base(options, authorizationOptions)
{
Options = options.Options;
AuthorizationOptions = authorizationOptions.Options;
}
public AuthorizationOptions AuthorizationOptions { get; }
public MvcOptions Options { get; }
public new IEnumerable<ControllerModel> 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<ActionModel> BuildActionModels(TypeInfo typeInfo, MethodInfo methodInfo)
{
return base.BuildActionModels(typeInfo, methodInfo);
}
public new ActionModel CreateActionModel(MethodInfo methodInfo, IReadOnlyList<object> 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);
}
}
}
}

View File

@ -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<ControllerActionFilter>(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<IOptions<CorsOptions>>();
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<FromQueryAttribute>(attribute);
}
[Fact]
public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
{
// Arrange
var corsOptions = new CorsOptions();
corsOptions.AddPolicy("policy", new CorsPolicy());
var mockOptions = new Mock<IOptions<CorsOptions>>();
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<ProducesAttribute>(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();
}
}
}
}

View File

@ -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<IFilter> 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<ActionDescriptor> 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();
}

View File

@ -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)

View File

@ -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<AuthorizationOptions>
{
public AuthorizationOptions Options { get; } = new AuthorizationOptions();
public AuthorizationOptions GetNamedOptions(string name)
{
return Options;
}
}
}

View File

@ -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<IFilter> filters)
{
var filterList = new List<IFilter>();
Filters = filterList;
if (filters != null)
{
filterList.AddRange(filters);
}
}
public IReadOnlyList<IFilter> Filters { get; private set; }
}
}

View File

@ -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),
};
}
}

View File

@ -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<IGlobalFilterProvider>();
filterProvider
.SetupGet(fp => fp.Filters)
.Returns(new List<IFilter>());
var options = new MvcOptions();
@ -389,10 +384,18 @@ namespace System.Web.Http
.SetupGet(o => o.Options)
.Returns(options);
var authorizationOptionsAccessor = new Mock<IOptions<AuthorizationOptions>>();
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;