moving global filters to options
This commit is contained in:
parent
a468986155
commit
7ed2de297e
13
Mvc.sln
13
Mvc.sln
|
|
@ -74,6 +74,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AntiForgeryWebSite", "test\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AddServicesWebSite", "test\WebSites\AddServicesWebSite\AddServicesWebSite.kproj", "{6A0B65CE-6B01-40D0-840D-EFF3680D1547}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AddServicesWebSite", "test\WebSites\AddServicesWebSite\AddServicesWebSite.kproj", "{6A0B65CE-6B01-40D0-840D-EFF3680D1547}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FiltersWebSite", "test\WebSites\FiltersWebSite\FiltersWebSite.kproj", "{1976AC4A-FEA4-4587-A158-D9F79736D2B6}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -374,6 +376,16 @@ Global
|
||||||
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|x86.ActiveCfg = Release|Any CPU
|
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -409,5 +421,6 @@ Global
|
||||||
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{A353B17E-A940-4CE8-8BF9-179E24A9041F} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{A353B17E-A940-4CE8-8BF9-179E24A9041F} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{6A0B65CE-6B01-40D0-840D-EFF3680D1547} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{6A0B65CE-6B01-40D0-840D-EFF3680D1547} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
|
{1976AC4A-FEA4-4587-A158-D9F79736D2B6} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>51929</DevelopmentServerPort>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||||
<DevelopmentServerPort>57394</DevelopmentServerPort>
|
<DevelopmentServerPort>57394</DevelopmentServerPort>
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using MvcSample.Web.Filters;
|
using MvcSample.Web.Filters;
|
||||||
using MvcSample.Web.Services;
|
using MvcSample.Web.Services;
|
||||||
|
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Microsoft.Framework.DependencyInjection.Autofac;
|
using Microsoft.Framework.DependencyInjection.Autofac;
|
||||||
using Microsoft.Framework.OptionsModel;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace MvcSample.Web
|
namespace MvcSample.Web
|
||||||
|
|
@ -42,6 +42,10 @@ namespace MvcSample.Web
|
||||||
// sample's assemblies are loaded. This prevents loading controllers from other assemblies
|
// sample's assemblies are loaded. This prevents loading controllers from other assemblies
|
||||||
// when the sample is used in the Functional Tests.
|
// when the sample is used in the Functional Tests.
|
||||||
services.AddTransient<IControllerAssemblyProvider, TestAssemblyProvider<Startup>>();
|
services.AddTransient<IControllerAssemblyProvider, TestAssemblyProvider<Startup>>();
|
||||||
|
services.SetupOptions<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
||||||
|
});
|
||||||
|
|
||||||
// Create the autofac container
|
// Create the autofac container
|
||||||
ContainerBuilder builder = new ContainerBuilder();
|
ContainerBuilder builder = new ContainerBuilder();
|
||||||
|
|
@ -72,6 +76,11 @@ namespace MvcSample.Web
|
||||||
// sample's assemblies are loaded. This prevents loading controllers from other assemblies
|
// sample's assemblies are loaded. This prevents loading controllers from other assemblies
|
||||||
// when the sample is used in the Functional Tests.
|
// when the sample is used in the Functional Tests.
|
||||||
services.AddTransient<IControllerAssemblyProvider, TestAssemblyProvider<Startup>>();
|
services.AddTransient<IControllerAssemblyProvider, TestAssemblyProvider<Startup>>();
|
||||||
|
|
||||||
|
services.SetupOptions<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public class Controller : IActionFilter, IAsyncActionFilter
|
public class Controller : IActionFilter, IAsyncActionFilter, IOrderedFilter
|
||||||
{
|
{
|
||||||
private DynamicViewData _viewBag;
|
private DynamicViewData _viewBag;
|
||||||
|
|
||||||
|
|
@ -80,6 +80,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int IOrderedFilter.Order
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Controller-filter methods run closest the action by default.
|
||||||
|
return int.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="ViewResult"/> object that renders a view to the response.
|
/// Creates a <see cref="ViewResult"/> object that renders a view to the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,9 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
{
|
{
|
||||||
public class DefaultFilterProvider : INestedProvider<FilterProviderContext>
|
public class DefaultFilterProvider : INestedProvider<FilterProviderContext>
|
||||||
{
|
{
|
||||||
private readonly ITypeActivator _typeActivator;
|
public DefaultFilterProvider(IServiceProvider serviceProvider)
|
||||||
|
|
||||||
public DefaultFilterProvider(IServiceProvider serviceProvider, ITypeActivator typeActivator)
|
|
||||||
{
|
{
|
||||||
ServiceProvider = serviceProvider;
|
ServiceProvider = serviceProvider;
|
||||||
_typeActivator = typeActivator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order
|
public int Order
|
||||||
|
|
@ -78,15 +75,6 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
|
|
||||||
private void InsertControllerAsFilter(FilterProviderContext context, IFilter controllerFilter)
|
private void InsertControllerAsFilter(FilterProviderContext context, IFilter controllerFilter)
|
||||||
{
|
{
|
||||||
// If the controller implements a filter, and doesn't specify order, then it should
|
|
||||||
// run closest to the action.
|
|
||||||
var order = Int32.MaxValue;
|
|
||||||
var orderedControllerFilter = controllerFilter as IOrderedFilter;
|
|
||||||
if (orderedControllerFilter != null)
|
|
||||||
{
|
|
||||||
order = orderedControllerFilter.Order;
|
|
||||||
}
|
|
||||||
|
|
||||||
var descriptor = new FilterDescriptor(controllerFilter, FilterScope.Controller);
|
var descriptor = new FilterDescriptor(controllerFilter, FilterScope.Controller);
|
||||||
var item = new FilterItem(descriptor, controllerFilter);
|
var item = new FilterItem(descriptor, controllerFilter);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
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(IOptionsAccessor<MvcOptions> optionsAccessor)
|
||||||
|
{
|
||||||
|
var filters = optionsAccessor.Options.Filters;
|
||||||
|
_filters = filters.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<IFilter> Filters
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _filters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for adding filters to the global filters collection.
|
||||||
|
/// </summary>
|
||||||
|
public static class FilterCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a type representing an <see cref="IFilter"/> to a filter collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filters">A collection of <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="filterType">Type representing an <see cref="IFilter"/>.</param>
|
||||||
|
/// <returns>An <see cref="IFilter"/> representing the added type.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Filter instances will be created using <see cref="Microsoft.Framework.DependencyInjection.ITypeActivator"/>.
|
||||||
|
/// Use <see cref="AddService(ICollection{IFilter}, Type)"/> to register a service as a filter.
|
||||||
|
/// </remarks>
|
||||||
|
public static IFilter Add(
|
||||||
|
[NotNull] this ICollection<IFilter> filters,
|
||||||
|
[NotNull] Type filterType)
|
||||||
|
{
|
||||||
|
return Add(filters, filterType, order: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a type representing an <see cref="IFilter"/> to a filter collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filters">A collection of <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="filterType">Type representing an <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="order">The order of the added filter.</param>
|
||||||
|
/// <returns>An <see cref="IFilter"/> representing the added type.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Filter instances will be created using <see cref="Microsoft.Framework.DependencyInjection.ITypeActivator"/>.
|
||||||
|
/// Use <see cref="AddService(ICollection{IFilter}, Type)"/> to register a service as a filter.
|
||||||
|
/// </remarks>
|
||||||
|
public static IFilter Add(
|
||||||
|
[NotNull] this ICollection<IFilter> filters,
|
||||||
|
[NotNull] Type filterType,
|
||||||
|
int order)
|
||||||
|
{
|
||||||
|
if (!typeof(IFilter).IsAssignableFrom(filterType))
|
||||||
|
{
|
||||||
|
var message = Resources.FormatTypeMustDeriveFromType(filterType.FullName, typeof(IFilter).FullName);
|
||||||
|
throw new ArgumentException(message, nameof(filterType));
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = new TypeFilterAttribute(filterType) { Order = order };
|
||||||
|
filters.Add(filter);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a type representing an <see cref="IFilter"/> to a filter collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filters">A collection of <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="filterType">Type representing an <see cref="IFilter"/>.</param>
|
||||||
|
/// <returns>An <see cref="IFilter"/> representing the added service type.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Filter instances will created through dependency injection. Use
|
||||||
|
/// <see cref="AddService(ICollection{IFilter}, Type)"/> to register a service that will be created via
|
||||||
|
/// type activation.
|
||||||
|
/// </remarks>
|
||||||
|
public static IFilter AddService(
|
||||||
|
[NotNull] this ICollection<IFilter> filters,
|
||||||
|
[NotNull] Type filterType)
|
||||||
|
{
|
||||||
|
return AddService(filters, filterType, order: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a type representing an <see cref="IFilter"/> to a filter collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filters">A collection of <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="filterType">Type representing an <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="order">The order of the added filter.</param>
|
||||||
|
/// <returns>An <see cref="IFilter"/> representing the added service type.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Filter instances will created through dependency injection. Use
|
||||||
|
/// <see cref="AddService(ICollection{IFilter}, Type)"/> to register a service that will be created via
|
||||||
|
/// type activation.
|
||||||
|
/// </remarks>
|
||||||
|
public static IFilter AddService(
|
||||||
|
[NotNull] this ICollection<IFilter> filters,
|
||||||
|
[NotNull] Type filterType,
|
||||||
|
int order)
|
||||||
|
{
|
||||||
|
if (!typeof(IFilter).IsAssignableFrom(filterType))
|
||||||
|
{
|
||||||
|
var message = Resources.FormatTypeMustDeriveFromType(filterType.FullName, typeof(IFilter).FullName);
|
||||||
|
throw new ArgumentException(message, nameof(filterType));
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = new ServiceFilterAttribute(filterType) { Order = order };
|
||||||
|
filters.Add(filter);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,34 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Descriptor for an <see cref="IFilter"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="FilterDescriptor"/> describes an <see cref="IFilter"/> with an order and scope.
|
||||||
|
///
|
||||||
|
/// Order and scope control the execution order of filters. Filters with a higher value of Order execute
|
||||||
|
/// later in the pipeline.
|
||||||
|
///
|
||||||
|
/// When filters have the same Order, the Scope value is used to determine the order of execution. Filters
|
||||||
|
/// with a higher value of Scope execute later in the pipeline. See <see cref="FilterScope"/> for commonly
|
||||||
|
/// used scopes.
|
||||||
|
///
|
||||||
|
/// For <see cref="IExceptionFilter"/> implementions, the filter runs only after an exception has occurred,
|
||||||
|
/// and so the observed order of execution will be opposite that of other filters.
|
||||||
|
/// </remarks>
|
||||||
public class FilterDescriptor
|
public class FilterDescriptor
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="FilterDescriptor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter">The <see cref="IFilter"/>.</param>
|
||||||
|
/// <param name="filterScope">The filter scope.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the <paramref name="filter"/> implements <see cref="IOrderedFilter"/>, then the value of
|
||||||
|
/// <see cref="Order"/> will be taken from <see cref="IOrderedFilter.Order"/>. Otherwise the value
|
||||||
|
/// of <see cref="Order"/> will default to <c>0</c>.
|
||||||
|
/// </remarks>
|
||||||
public FilterDescriptor([NotNull] IFilter filter, int filterScope)
|
public FilterDescriptor([NotNull] IFilter filter, int filterScope)
|
||||||
{
|
{
|
||||||
Filter = filter;
|
Filter = filter;
|
||||||
|
|
@ -18,10 +44,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="IFilter"/> instance.
|
||||||
|
/// </summary>
|
||||||
public IFilter Filter { get; private set; }
|
public IFilter Filter { get; private set; }
|
||||||
|
|
||||||
public int Order { get; private set; }
|
/// <summary>
|
||||||
|
/// The filter order.
|
||||||
|
/// </summary>
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The filter scope.
|
||||||
|
/// </summary>
|
||||||
public int Scope { get; private set; }
|
public int Scope { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||||
using Microsoft.AspNet.Mvc.ReflectedModelBuilder;
|
using Microsoft.AspNet.Mvc.ReflectedModelBuilder;
|
||||||
|
|
@ -25,6 +26,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
ValueProviderFactories = new List<ValueProviderFactoryDescriptor>();
|
ValueProviderFactories = new List<ValueProviderFactoryDescriptor>();
|
||||||
OutputFormatters = new List<OutputFormatterDescriptor>();
|
OutputFormatters = new List<OutputFormatterDescriptor>();
|
||||||
InputFormatters = new List<InputFormatterDescriptor>();
|
InputFormatters = new List<InputFormatterDescriptor>();
|
||||||
|
Filters = new List<IFilter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -51,13 +53,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a list of the <see cref="OutputFormatterDescriptor" /> which are used to construct
|
/// Gets a list of <see cref="IFilter"/> which are used to construct filters that
|
||||||
|
/// apply to all actions.
|
||||||
|
/// </summary>
|
||||||
|
public ICollection<IFilter> Filters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of the <see cref="OutputFormatterDescriptor" /> which are used to construct
|
||||||
/// a list of <see cref="IOutputFormatter"/> by <see cref="IOutputFormattersProvider"/>.
|
/// a list of <see cref="IOutputFormatter"/> by <see cref="IOutputFormattersProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
|
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
|
/// Gets a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
|
||||||
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormattersProvider"/>.
|
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormattersProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
|
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
|
||||||
|
|
@ -86,13 +94,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a list of the <see cref="ModelBinderDescriptor" /> used by the
|
/// Gets a list of the <see cref="ModelBinderDescriptor" /> used by the
|
||||||
/// <see cref="ModelBinding.CompositeModelBinder" />.
|
/// <see cref="ModelBinding.CompositeModelBinder" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ModelBinderDescriptor> ModelBinders { get; private set; }
|
public List<ModelBinderDescriptor> ModelBinders { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a list of the <see cref="ModelValidatorProviderDescriptor" />s used by
|
/// Gets a list of the <see cref="ModelValidatorProviderDescriptor" />s used by
|
||||||
/// <see cref="ModelBinding.CompositeModelValidatorProvider"/>.
|
/// <see cref="ModelBinding.CompositeModelValidatorProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ModelValidatorProviderDescriptor> ModelValidatorProviders { get; } =
|
public List<ModelValidatorProviderDescriptor> ModelValidatorProviders { get; } =
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.AspNet.Mvc.ReflectedModelBuilder;
|
using Microsoft.AspNet.Mvc.ReflectedModelBuilder;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
@ -27,19 +28,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
private readonly IControllerAssemblyProvider _controllerAssemblyProvider;
|
private readonly IControllerAssemblyProvider _controllerAssemblyProvider;
|
||||||
private readonly IActionDiscoveryConventions _conventions;
|
private readonly IActionDiscoveryConventions _conventions;
|
||||||
private readonly IEnumerable<IFilter> _globalFilters;
|
private readonly IReadOnlyList<IFilter> _globalFilters;
|
||||||
private readonly IEnumerable<IReflectedApplicationModelConvention> _modelConventions;
|
private readonly IEnumerable<IReflectedApplicationModelConvention> _modelConventions;
|
||||||
private readonly IInlineConstraintResolver _constraintResolver;
|
private readonly IInlineConstraintResolver _constraintResolver;
|
||||||
|
|
||||||
public ReflectedActionDescriptorProvider(IControllerAssemblyProvider controllerAssemblyProvider,
|
public ReflectedActionDescriptorProvider(IControllerAssemblyProvider controllerAssemblyProvider,
|
||||||
IActionDiscoveryConventions conventions,
|
IActionDiscoveryConventions conventions,
|
||||||
IEnumerable<IFilter> globalFilters,
|
IGlobalFilterProvider globalFilters,
|
||||||
IOptionsAccessor<MvcOptions> optionsAccessor,
|
IOptionsAccessor<MvcOptions> optionsAccessor,
|
||||||
IInlineConstraintResolver constraintResolver)
|
IInlineConstraintResolver constraintResolver)
|
||||||
{
|
{
|
||||||
_controllerAssemblyProvider = controllerAssemblyProvider;
|
_controllerAssemblyProvider = controllerAssemblyProvider;
|
||||||
_conventions = conventions;
|
_conventions = conventions;
|
||||||
_globalFilters = globalFilters ?? Enumerable.Empty<IFilter>();
|
_globalFilters = globalFilters.Filters;
|
||||||
_modelConventions = optionsAccessor.Options.ApplicationModelConventions;
|
_modelConventions = optionsAccessor.Options.ApplicationModelConventions;
|
||||||
_constraintResolver = constraintResolver;
|
_constraintResolver = constraintResolver;
|
||||||
}
|
}
|
||||||
|
|
@ -352,8 +353,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
IEnumerable<IFilter> controllerFilters,
|
IEnumerable<IFilter> controllerFilters,
|
||||||
IEnumerable<IFilter> globalFilters)
|
IEnumerable<IFilter> globalFilters)
|
||||||
{
|
{
|
||||||
actionDescriptor.FilterDescriptors = actionFilters
|
actionDescriptor.FilterDescriptors =
|
||||||
.Select(f => new FilterDescriptor(f, FilterScope.Action))
|
actionFilters.Select(f => new FilterDescriptor(f, FilterScope.Action))
|
||||||
.Concat(controllerFilters.Select(f => new FilterDescriptor(f, FilterScope.Controller)))
|
.Concat(controllerFilters.Select(f => new FilterDescriptor(f, FilterScope.Controller)))
|
||||||
.Concat(globalFilters.Select(f => new FilterDescriptor(f, FilterScope.Global)))
|
.Concat(globalFilters.Select(f => new FilterDescriptor(f, FilterScope.Global)))
|
||||||
.OrderBy(d => d, FilterDescriptorOrderComparer.Comparer)
|
.OrderBy(d => d, FilterDescriptorOrderComparer.Comparer)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,16 @@ namespace Microsoft.AspNet.Mvc.ReflectedModelBuilder
|
||||||
// is needed to so that the result of ToList() is List<object>
|
// is needed to so that the result of ToList() is List<object>
|
||||||
Attributes = actionMethod.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
Attributes = actionMethod.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
||||||
|
|
||||||
Filters = Attributes.OfType<IFilter>().ToList();
|
Filters = Attributes
|
||||||
|
.OfType<IFilter>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var routeTemplateAttribute = Attributes.OfType<IRouteTemplateProvider>().FirstOrDefault();
|
||||||
|
if (routeTemplateAttribute != null)
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new ReflectedAttributeRouteModel(routeTemplateAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
HttpMethods = new List<string>();
|
HttpMethods = new List<string>();
|
||||||
Parameters = new List<ReflectedParameterModel>();
|
Parameters = new List<ReflectedParameterModel>();
|
||||||
}
|
}
|
||||||
|
|
@ -39,4 +48,4 @@ namespace Microsoft.AspNet.Mvc.ReflectedModelBuilder
|
||||||
|
|
||||||
public ReflectedAttributeRouteModel AttributeRouteModel { get; set; }
|
public ReflectedAttributeRouteModel AttributeRouteModel { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ namespace Microsoft.AspNet.Mvc.ReflectedModelBuilder
|
||||||
// is needed to so that the result of ToList() is List<object>
|
// is needed to so that the result of ToList() is List<object>
|
||||||
Attributes = ControllerType.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
Attributes = ControllerType.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
||||||
|
|
||||||
Filters = Attributes.OfType<IFilter>().ToList();
|
Filters = Attributes
|
||||||
|
.OfType<IFilter>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
RouteConstraints = Attributes.OfType<RouteConstraintAttribute>().ToList();
|
RouteConstraints = Attributes.OfType<RouteConstraintAttribute>().ToList();
|
||||||
|
|
||||||
AttributeRoutes = Attributes.OfType<IRouteTemplateProvider>()
|
AttributeRoutes = Attributes.OfType<IRouteTemplateProvider>()
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,4 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IViewEngine> ViewEngines { get; }
|
IReadOnlyList<IViewEngine> ViewEngines { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
yield return describe.Instance<JsonOutputFormatter>(
|
yield return describe.Instance<JsonOutputFormatter>(
|
||||||
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), indent: false));
|
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), indent: false));
|
||||||
|
|
||||||
|
// The IGlobalFilterProvider is used to build the action descriptors (likely once) and so should
|
||||||
|
// remain transient to avoid keeping it in memory.
|
||||||
|
yield return describe.Transient<IGlobalFilterProvider, DefaultGlobalFilterProvider>();
|
||||||
|
|
||||||
yield return describe.Transient<INestedProvider<FilterProviderContext>, DefaultFilterProvider>();
|
yield return describe.Transient<INestedProvider<FilterProviderContext>, DefaultFilterProvider>();
|
||||||
|
|
||||||
yield return describe.Transient<IModelValidatorProviderProvider, DefaultModelValidatorProviderProvider>();
|
yield return describe.Transient<IModelValidatorProviderProvider, DefaultModelValidatorProviderProvider>();
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
return new ReflectedActionDescriptorProvider(
|
return new ReflectedActionDescriptorProvider(
|
||||||
controllerAssemblyProvider,
|
controllerAssemblyProvider,
|
||||||
actionDiscoveryConventions,
|
actionDiscoveryConventions,
|
||||||
null,
|
new TestGlobalFilterProvider(),
|
||||||
new MockMvcOptionsAccessor(),
|
new MockMvcOptionsAccessor(),
|
||||||
Mock.Of<IInlineConstraintResolver>());
|
Mock.Of<IInlineConstraintResolver>());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
return new ReflectedActionDescriptorProvider(
|
return new ReflectedActionDescriptorProvider(
|
||||||
controllerAssemblyProvider.Object,
|
controllerAssemblyProvider.Object,
|
||||||
actionDiscoveryConventions,
|
actionDiscoveryConventions,
|
||||||
null,
|
new TestGlobalFilterProvider(),
|
||||||
new MockMvcOptionsAccessor(),
|
new MockMvcOptionsAccessor(),
|
||||||
Mock.Of<IInlineConstraintResolver>());
|
Mock.Of<IInlineConstraintResolver>());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,224 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Design;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Xunit;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Filters
|
||||||
|
{
|
||||||
|
public class DefaultFilterProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_UsesFilter_WhenItsNotIFilterFactory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = Mock.Of<IFilter>();
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filter, FilterScope.Global)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
//System.Diagnostics.Debugger.Launch();
|
||||||
|
//System.Diagnostics.Debugger.Break();
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var item = Assert.Single(results);
|
||||||
|
Assert.Same(filter, item.Filter);
|
||||||
|
Assert.Same(filter, item.Descriptor.Filter);
|
||||||
|
Assert.Equal(0, item.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_UsesFilterFactory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = Mock.Of<IFilter>();
|
||||||
|
|
||||||
|
var filterFactory = new Mock<IFilterFactory>();
|
||||||
|
filterFactory
|
||||||
|
.Setup(ff => ff.CreateInstance(It.IsAny<IServiceProvider>()))
|
||||||
|
.Returns(filter);
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filterFactory.Object, FilterScope.Global)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var item = Assert.Single(results);
|
||||||
|
Assert.Same(filter, item.Filter);
|
||||||
|
Assert.Same(filterFactory.Object, item.Descriptor.Filter);
|
||||||
|
Assert.Equal(0, item.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_UsesFilterFactory_WithOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = Mock.Of<IFilter>();
|
||||||
|
|
||||||
|
var filterFactory = new Mock<IFilterFactory>();
|
||||||
|
filterFactory
|
||||||
|
.Setup(ff => ff.CreateInstance(It.IsAny<IServiceProvider>()))
|
||||||
|
.Returns(filter);
|
||||||
|
|
||||||
|
filterFactory.As<IOrderedFilter>().SetupGet(ff => ff.Order).Returns(17);
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filterFactory.Object, FilterScope.Global)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var item = Assert.Single(results);
|
||||||
|
Assert.Same(filter, item.Filter);
|
||||||
|
Assert.Same(filterFactory.Object, item.Descriptor.Filter);
|
||||||
|
Assert.Equal(17, item.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_UsesFilterFactory_WithIFilterContainer()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new Mock<IFilterContainer>();
|
||||||
|
filter.SetupAllProperties();
|
||||||
|
|
||||||
|
var filterFactory = new Mock<IFilterFactory>();
|
||||||
|
filterFactory
|
||||||
|
.Setup(ff => ff.CreateInstance(It.IsAny<IServiceProvider>()))
|
||||||
|
.Returns(filter.As<IFilter>().Object);
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filterFactory.Object, FilterScope.Global)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var item = Assert.Single(results);
|
||||||
|
Assert.Same(filter.Object, item.Filter);
|
||||||
|
Assert.Same(filterFactory.Object, ((IFilterContainer)item.Filter).FilterDefinition);
|
||||||
|
Assert.Same(filterFactory.Object, item.Descriptor.Filter);
|
||||||
|
Assert.Equal(0, item.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_InsertsController_DefaultOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter1 = new Mock<IOrderedFilter>();
|
||||||
|
filter1.SetupGet(f => f.Order).Returns(int.MaxValue);
|
||||||
|
|
||||||
|
var filter2 = new Mock<IOrderedFilter>();
|
||||||
|
filter2.SetupGet(f => f.Order).Returns(int.MaxValue);
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filter1.Object, FilterScope.Global)),
|
||||||
|
new FilterItem(new FilterDescriptor(filter2.Object, FilterScope.Action)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var controller = new Controller();
|
||||||
|
context.ActionContext.Controller = controller;
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerItem = results[1];
|
||||||
|
Assert.Same(controller, controllerItem.Filter);
|
||||||
|
Assert.Same(controller, controllerItem.Descriptor.Filter);
|
||||||
|
Assert.Equal(FilterScope.Controller, controllerItem.Descriptor.Scope);
|
||||||
|
Assert.Equal(Int32.MaxValue, controllerItem.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFilterProvider_InsertsController_CustomOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter1 = new Mock<IOrderedFilter>();
|
||||||
|
filter1.SetupGet(f => f.Order).Returns(0);
|
||||||
|
|
||||||
|
var filter2 = new Mock<IOrderedFilter>();
|
||||||
|
filter2.SetupGet(f => f.Order).Returns(int.MaxValue);
|
||||||
|
|
||||||
|
var context = CreateFilterContext(new List<FilterItem>()
|
||||||
|
{
|
||||||
|
new FilterItem(new FilterDescriptor(filter1.Object, FilterScope.Global)),
|
||||||
|
new FilterItem(new FilterDescriptor(filter2.Object, FilterScope.Action)),
|
||||||
|
});
|
||||||
|
|
||||||
|
var controller = new Mock<IOrderedFilter>();
|
||||||
|
controller.SetupGet(f => f.Order).Returns(17);
|
||||||
|
|
||||||
|
context.ActionContext.Controller = controller.Object;
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Invoke(context, () => { });
|
||||||
|
var results = context.Results;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerItem = results[1];
|
||||||
|
Assert.Same(controller.Object, controllerItem.Filter);
|
||||||
|
Assert.Same(controller.Object, controllerItem.Descriptor.Filter);
|
||||||
|
Assert.Equal(FilterScope.Controller, controllerItem.Descriptor.Scope);
|
||||||
|
Assert.Equal(17, controllerItem.Descriptor.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultFilterProvider CreateProvider()
|
||||||
|
{
|
||||||
|
var services = new ServiceContainer();
|
||||||
|
|
||||||
|
return new DefaultFilterProvider(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FilterProviderContext CreateFilterContext(List<FilterItem> items)
|
||||||
|
{
|
||||||
|
var actionContext = CreateActionContext();
|
||||||
|
actionContext.ActionDescriptor.FilterDescriptors = new List<FilterDescriptor>(
|
||||||
|
items.Select(item => item.Descriptor));
|
||||||
|
|
||||||
|
return new FilterProviderContext(actionContext, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionContext CreateActionContext()
|
||||||
|
{
|
||||||
|
return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
public class FilterCollectionExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Add_UsesTypeFilterAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var added = collection.Add(typeof(MyFilter));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var typeFilter = Assert.IsType<TypeFilterAttribute>(added);
|
||||||
|
Assert.Equal(typeof(MyFilter), typeFilter.ImplementationType);
|
||||||
|
Assert.Same(typeFilter, Assert.Single(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_WithOrder_SetsOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var added = collection.Add(typeof(MyFilter), 17);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(17, Assert.IsAssignableFrom<IOrderedFilter>(added).Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_ThrowsOnNonIFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
var expectedMessage =
|
||||||
|
"The type 'Microsoft.AspNet.Mvc.FilterCollectionExtensionsTest+NonFilter' must derive from " +
|
||||||
|
"'Microsoft.AspNet.Mvc.IFilter'." + Environment.NewLine +
|
||||||
|
"Parameter name: filterType";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<ArgumentException>(() => { collection.Add(typeof(NonFilter)); });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddService_UsesServiceFilterAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var added = collection.AddService(typeof(MyFilter));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var serviceFilter = Assert.IsType<ServiceFilterAttribute>(added);
|
||||||
|
Assert.Equal(typeof(MyFilter), serviceFilter.ServiceType);
|
||||||
|
Assert.Same(serviceFilter, Assert.Single(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddService_SetsOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var added = collection.AddService(typeof(MyFilter), 17);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(17, Assert.IsAssignableFrom<IOrderedFilter>(added).Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddService_ThrowsOnNonIFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new Collection<IFilter>();
|
||||||
|
|
||||||
|
var expectedMessage =
|
||||||
|
"The type 'Microsoft.AspNet.Mvc.FilterCollectionExtensionsTest+NonFilter' must derive from " +
|
||||||
|
"'Microsoft.AspNet.Mvc.IFilter'." + Environment.NewLine +
|
||||||
|
"Parameter name: filterType";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<ArgumentException>(() => { collection.AddService(typeof(NonFilter)); });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyFilter : IFilter, IOrderedFilter
|
||||||
|
{
|
||||||
|
public int Order
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NonFilter
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Test
|
namespace Microsoft.AspNet.Mvc.Test
|
||||||
{
|
{
|
||||||
|
|
@ -1029,7 +1030,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var provider = new ReflectedActionDescriptorProvider(
|
var provider = new ReflectedActionDescriptorProvider(
|
||||||
assemblyProvider.Object,
|
assemblyProvider.Object,
|
||||||
conventions,
|
conventions,
|
||||||
filters,
|
new TestGlobalFilterProvider(filters),
|
||||||
new MockMvcOptionsAccessor(),
|
new MockMvcOptionsAccessor(),
|
||||||
Mock.Of<IInlineConstraintResolver>());
|
Mock.Of<IInlineConstraintResolver>());
|
||||||
|
|
||||||
|
|
@ -1049,7 +1050,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var provider = new ReflectedActionDescriptorProvider(
|
var provider = new ReflectedActionDescriptorProvider(
|
||||||
assemblyProvider.Object,
|
assemblyProvider.Object,
|
||||||
conventions,
|
conventions,
|
||||||
Enumerable.Empty<IFilter>(),
|
new TestGlobalFilterProvider(),
|
||||||
new MockMvcOptionsAccessor(),
|
new MockMvcOptionsAccessor(),
|
||||||
Mock.Of<IInlineConstraintResolver>());
|
Mock.Of<IInlineConstraintResolver>());
|
||||||
|
|
||||||
|
|
@ -1068,7 +1069,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var provider = new ReflectedActionDescriptorProvider(
|
var provider = new ReflectedActionDescriptorProvider(
|
||||||
assemblyProvider.Object,
|
assemblyProvider.Object,
|
||||||
conventions,
|
conventions,
|
||||||
null,
|
new TestGlobalFilterProvider(),
|
||||||
new MockMvcOptionsAccessor(),
|
new MockMvcOptionsAccessor(),
|
||||||
null);
|
null);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class FiltersTest
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services = TestHelper.CreateServices("FiltersWebSite");
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new FiltersWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ListAllFilters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/Products/GetPrice/5");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<decimal>(body);
|
||||||
|
|
||||||
|
Assert.Equal(19.95m, result);
|
||||||
|
|
||||||
|
var filters = response.Headers.GetValues("filters");
|
||||||
|
Assert.Equal(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
// This one uses order to set itself 'first' even though it appears on the controller
|
||||||
|
"FiltersWebSite.PassThroughActionFilter",
|
||||||
|
|
||||||
|
// Configured as global with default order
|
||||||
|
"FiltersWebSite.GlobalExceptionFilter",
|
||||||
|
|
||||||
|
// Configured on the controller with default order
|
||||||
|
"FiltersWebSite.PassThroughResultFilter",
|
||||||
|
|
||||||
|
// Configured on the action with default order
|
||||||
|
"FiltersWebSite.PassThroughActionFilter",
|
||||||
|
|
||||||
|
// The controller itself
|
||||||
|
"FiltersWebSite.ProductsController",
|
||||||
|
},
|
||||||
|
filters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
"AntiForgeryWebSite": "",
|
"AntiForgeryWebSite": "",
|
||||||
"BasicWebSite": "",
|
"BasicWebSite": "",
|
||||||
"CompositeViewEngine": "",
|
"CompositeViewEngine": "",
|
||||||
"ConnegWebsite": "",
|
"ConnegWebsite": "",
|
||||||
|
"FiltersWebSite": "",
|
||||||
"FormatterWebSite": "",
|
"FormatterWebSite": "",
|
||||||
"InlineConstraintsWebSite": "",
|
"InlineConstraintsWebSite": "",
|
||||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FiltersWebSite
|
||||||
|
{
|
||||||
|
// This controller will list the filters that are configured for each action in a header.
|
||||||
|
// This exercises the merging of filters with the global filters collection.
|
||||||
|
[PassThroughActionFilter(Order = -2)]
|
||||||
|
[PassThroughResultFilter]
|
||||||
|
public class ProductsController : Controller
|
||||||
|
{
|
||||||
|
[PassThroughActionFilter]
|
||||||
|
public decimal GetPrice(int id)
|
||||||
|
{
|
||||||
|
return 19.95m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
// Log the filter names in a header
|
||||||
|
context.HttpContext.Response.Headers.Add(
|
||||||
|
"filters",
|
||||||
|
context.Filters.Select(f => f.GetType().FullName).ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FiltersWebSite
|
||||||
|
{
|
||||||
|
public class GlobalExceptionFilter : IExceptionFilter
|
||||||
|
{
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
context.Result = new ContentResult()
|
||||||
|
{
|
||||||
|
Content = "GlobalExceptionFilter.OnException",
|
||||||
|
ContentType = "text/plain",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FiltersWebSite
|
||||||
|
{
|
||||||
|
public class PassThroughActionFilter : ActionFilterAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FiltersWebSite
|
||||||
|
{
|
||||||
|
public class PassThroughResultFilter : ResultFilterAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>1976ac4a-fea4-4587-a158-d9f79736d2b6</ProjectGuid>
|
||||||
|
<OutputType>Web</OutputType>
|
||||||
|
<RootNamespace>FiltersWebSite</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Console'">
|
||||||
|
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Web'">
|
||||||
|
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>25980</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
|
namespace FiltersWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var configuration = app.GetTestConfiguration();
|
||||||
|
|
||||||
|
app.UseServices(services =>
|
||||||
|
{
|
||||||
|
services.AddMvc(configuration);
|
||||||
|
|
||||||
|
services.SetupOptions<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add(new GlobalExceptionFilter());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Mvc": "",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": ""
|
||||||
|
},
|
||||||
|
"frameworks" : {
|
||||||
|
"aspnet50": { },
|
||||||
|
"aspnetcore50": { }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue