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