缺少 IAsyncActionFilter 中参数描述符上的 GetCustomAttributes



Using .NET Core 2.1.

我正在尝试访问IAsyncActionFilter内部操作参数的属性。

public IActionResult DoSomething([MyAttribute] MyParameter p) { ... }

在我的IAsyncActionFilter中,我想访问参数p上的MyAttribute,但GetCustomAttributes不存在。

public class MyActionFilter : IAsyncActionFilter
{
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// GetCustomAttributes does not exist here...
var attributes = context.ActionDescriptor.Parameters[0].GetCustomAttributes<MyAttribute>(); 
return next();
}
}

在 ASP.NET MVC 5.2 中,您可以使用 GetCustomAttributes:

https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.parameterdescriptor.getcustomattributes?view=aspnet-mvc-5.2#System_Web_Mvc_ParameterDescriptor_GetCustomAttributes_System_Boolean_

在 .NET Core 中实现相同目标的方法是什么?


更新 1

似乎我们可以将ActionDescriptor转换为ControllerActionDescriptor以访问底层MethodInfo,然后访问参数及其属性。

public class TempDataActionFilter : IAsyncActionFilter
{
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var actionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;
var parameters = 
from p in actionDescriptor.MethodInfo.GetParameters()
where p.GetCustomAttributes(typeof(MyAttribute), true) != null
select p;
var controller = context.Controller as Controller;
foreach (var p in parameters)
{
// Do something with the parameters that have an attribute 
}
return next();
}
}

这感觉不对。看到Microsoft自己的文档中提出了这种类型的解决方案,我总是感到沮丧。这是一个等待发生的运行时错误。有没有更好的方法?

方法 1

似乎您正在尝试绑定操作参数。如果是这种情况,ModelBinding优先于筛选器,因此您不必将ActionDescriptor转换为ControllerActionDescriptor来检查参数是否具有指定的属性,

在你的场景中,一种更简单和更安全的方法是让你的FromTempDataAttribute实现IBindingSourceMetadata,以指示你想要绑定来自TempData的数据:

internal class FromTempDataAttribute : Attribute, IBindingSourceMetadata
{
public static readonly BindingSource Instance = new BindingSource(
"id-FromTempData",
"TempData Binding Source",
true,
true
);
public BindingSource BindingSource {get{
return FromTempDataAttribute.Instance;
}} 
}

然后创建一个模型绑定程序和相关的提供程序:

public class MyFromTempDataModelBinder : IModelBinder
{
private readonly IServiceProvider sp;
public MyFromTempDataModelBinder(IServiceProvider sp)
{
this.sp = sp;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var factory = this.sp.GetRequiredService<ITempDataDictionaryFactory>();
var tempData = factory.GetTempData(bindingContext.HttpContext);
var name = bindingContext.FieldName;
var o = tempData.Peek(name);
if (o == null) {
bindingContext.ModelState.AddModelError(name, $"cannot get {name} from TempData");
} else {
var result = Convert.ChangeType(o,bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
return Task.CompletedTask;
}
}
public class FromTempDataBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
var has = context.BindingInfo?.BindingSource == FromTempDataAttribute.Instance;
if(has){
return new BinderTypeModelBinder(typeof(MyFromTempDataModelBinder));
}
return null;
}
}

如果context.BindingInfo.BindingSource等于必需属性,则提供程序返回MyFromTempDataModelBinder的实例。

另外,不要忘记在您的初创公司中注册此提供程序:

services.AddMvc(opts => {
opts.ModelBinderProviders.Insert(0, new FromTempDataBinderProvider());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

最后,您可以自动获取数据:

public IActionResult Test([FromTempDataAttribute] string a, string b )
{
return Json(new {A = a, B = b,});
}

方法2

如果你坚持使用 Filter,你也可以让FromTempDataAttribute像我们上面所做的那样实现IBindingSourceMetadata接口,然后你可以得到这些参数如下:

public class TempDataActionFilter : IAsyncActionFilter
{
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var parameteres = context.ActionDescriptor.Parameters.Where(p => p.BindingInfo?.BindingSource == FromTempDataAttribute.Instance);
foreach(var p in parameteres){
// ...
}
return next();
}
}

相关内容

  • 没有找到相关文章

最新更新