我定义了两个异步的IAsyncActionFilter,分别用于参数校验以及返回结果包装;
我遇到的问题是:两个筛选器的调用顺序并没有按照 Order大小即先执行DataValidationFilter.Before 然后执行SucceedResultFilter.Before而是按照注入服务时调用AddDataValidation和AddUnifyResult的先后顺序执行。
例如先调用AddUnifyResult则先执行SucceedResultFilter.Before
筛选器定义如下
// 参数校验
public class DataValidationFilter(IOptions<ApiBehaviorOptions> options): IAsyncActionFilter, IOrderedFilter
{
private const int FilterOrder = -1000;
private readonly ApiBehaviorOptions _apiBehaviorOptions = options.Value;
public int Order => FilterOrder;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// do something
}
}
// 返回结果包装
public class SucceedResultFilter : IAsyncActionFilter, IOrderedFilter
{
private const int FilterOrder = 8888;
public int Order => FilterOrder;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// do something
}
添加两个筛选器的方式如下
/// <summary>
/// 添加规范化结果服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddUnifyResult(this IServiceCollection services)
{
services.Configure<MvcOptions>(options =>
{
options.Filters.Add<SucceedResultFilter>();
options.Conventions.Add(new UnifyResultApplicationModelConvention());
});
return services;
}
/// <summary>
/// 添加全局数据验证
/// </summary>
public static IServiceCollection AddDataValidation(this IServiceCollection services, Action<DataValidationOptions>? options = null)
{
var configureOptions = new DataValidationOptions();
options?.Invoke(configureOptions);
if (configureOptions.GlobalEnabled)
{
services.Configure<ApiBehaviorOptions>(opt =>
{
opt.SuppressMapClientErrors = configureOptions.SuppressMapClientErrors;
opt.SuppressModelStateInvalidFilter = configureOptions.SuppressModelStateInvalidFilter;
});
services.Configure<MvcOptions>(opt =>
{
opt.Filters.Add<DataValidationFilter>();
opt.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = configureOptions.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes;
});
}
return services;
}
你可以debug 看一下 ControllerActionDescriptor 上面的FilterDescriptors属性 这个list里面的filters 基本就等于最终执行的filters(多少个,和顺序)
ControllerActionDescriptor就是 ControllerBase.ControllerContext.ActionDescriptor属性.
一开始就看过了,这个顺序就是Filter的注入顺序
@DengQinwen:
用 options.Filters.Add<TFilter>(order);
看起来你正在尝试通过设置Order
属性来控制IAsyncActionFilter
的执行顺序,但这不起作用。实际上,根据官方文档,筛选器的执行顺序是由注册到 DI 容器的顺序决定的。也就是说,最先注册到DI容器的筛选器将首先被执行。
然而,Order
属性在某些情况下可以影响筛选器的执行顺序。特别是,在一个组中筛选器的执行顺序将由Order
属性确定。这意味着在同一个组中,具有较低Order
值的筛选器将首先执行。
如果你想要DataValidationFilter
筛选器在SucceedResultFilter
筛选器之前执行,你可以尝试让他们处于同一组,并且为DataValidationFilter
筛选器设置较低的Order
值。
在你的代码中,DataValidationFilter
的Order
值已经比SucceedResultFilter
的Order
值小,所以你需要确保他们注册到同一组。你可以通过创建自定义筛选器提供程序并将其添加到MVC选项的FilterProviders
集合来实现这一点。
下面是一个如何实现自定义筛选器提供程序的示例:
public class CustomFilterProvider : IFilterProvider
{
public int Order => -1000; // Make sure our provider runs first
public void OnProvidersExecuting(FilterProviderContext context)
{
// logic here
}
public void OnProvidersExecuted(FilterProviderContext context)
{
// logic here
}
}
然后在你的Startup.cs
文件中添加这个提供程序:
services.AddControllers(options =>
{
options.Filters.Add(new DataValidationFilter());
options.Filters.Add(new SucceedResultFilter());
options.FilterProviders.Clear();
options.FilterProviders.Add(new CustomFilterProvider());
});
这样,你就可以通过Order
属性来控制筛选器的执行顺序了。请注意,这只是一个示例,并可能需要根据你的具体需求进行调整。