Подход 1
Кажется, что вы пытаетесь связать аргументы действия. В этом случае ModelBinding предпочтительнее фильтра, поэтому вам не нужно приводить ActionDescriptor
к ControllerActionDescriptor
, чтобы проверить, имеет ли параметр указанный атрибут,
Inв вашем сценарии гораздо проще и безопаснее заставить ваш 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;
}}
}
, а затем создать ModelBinder и связанный с нимПровайдер:
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;
}
}
Провайдер возвращает экземпляр MyFromTempDataModelBinder
, если context.BindingInfo.BindingSource
равен необходимому атрибуту.
Также не забудьте зарегистрировать этого провайдера при запуске:
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
Если вы настаиваете на фильтре, вы можете также сделать 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();
}
}