[英]asp.net core custom attribute to get object from filter
I am having some ActionFilter s in my asp.net core mvc application, which validate some input data.我的 asp.net 核心 mvc 应用程序中有一些ActionFilter ,它们验证了一些输入数据。 For example, the client sends a userId inside the header, the filter loads that user from a repository, and validates, if the user exists, is active, has a license, and so on.
例如,客户端在 header 中发送一个 userId,过滤器从存储库加载该用户,并验证用户是否存在、是否处于活动状态、是否具有许可证等。 This filter is attached to a Controller method.
此过滤器附加到 Controller 方法。 The Controller method also needs to collect the same user object.
Controller方法还需要采集同一个用户object。 Because of performance, I want to pass that user object, collected inside the filter, to the controller, so the controller does not need to load the same user object again.
Because of performance, I want to pass that user object, collected inside the filter, to the controller, so the controller does not need to load the same user object again. I know there are ways to do so, like mentioned here .
我知道有办法做到这一点,就像这里提到的那样。
Because of clean code, I wonder if this would be possible, coding an attribute which defines what to retrieve, like the [FromBody] attribute does, for instance.由于代码干净,我想知道这是否可能,编码一个定义检索内容的属性,例如[FromBody]属性。
I could imagine this attribute named [FromFilter("User")] , which takes a parameter to specify the key inside the HttpContext.Items我可以想象这个名为[FromFilter("User")]的属性,它接受一个参数来指定 HttpContext.Items 中的键
A basic implementation could be something like this:一个基本的实现可能是这样的:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FromFilterAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
{
/// <inheritdoc />
public BindingSource BindingSource => BindingSource.Custom;
/// <inheritdoc />
public string Name { get; set; }
}
Neither do I know if this would a be a good idea, nor how to implement such a feature.我也不知道这是否是个好主意,也不知道如何实现这样的功能。 Hopefully someone can me point into the right direction
希望有人能指出正确的方向
As far as I know, we couldn't directly pass the object from filter to action.据我所知,我们不能直接将 object 从过滤器传递到操作。
In my opinion, the best solution is creating a custom model binding and then find the user from the repository and pass the user to the action.在我看来,最好的解决方案是创建自定义 model 绑定,然后从存储库中找到用户并将用户传递给操作。
Since the model binding is triggered before the filter, you could get the custom model binding result from the ActionExecutingContext
.由于在过滤器之前触发了 model 绑定,因此您可以从
ActionExecutingContext
获取自定义 model 绑定结果。
Order of execution:执行顺序:
UserModelBinder --> OnActionExecuting --> Index action UserModelBinder --> OnActionExecuting --> 索引动作
More details about to do it, you could refer to below codes:更多细节,你可以参考下面的代码:
Custom model binding:自定义 model 绑定:
public class UserModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var model = new UserModel()
{
id = 1,
name = "test"
};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Controller action and OnActionExecuting method: Controller 动作和 OnActionExecuting 方法:
OnActionExecuting: OnAction执行:
public override void OnActionExecuting(ActionExecutingContext context)
{
//ActionArguments["user"] is the parameter name of the action parameter
var user = context.ActionArguments["user"] as UserModel;
// Do something before the action executes.
base.OnActionExecuting(context);
}
Action method:动作方法:
public async Task<IActionResult> Index([ModelBinder(BinderType = typeof(UserModelBinder))] UserModel user)
{
int i =0;
return View();
}
Result:结果:
Filter onexecuting:过滤执行:
Action parameter:动作参数:
You can use HttpContext.Items
for this and create HttpContextItemsModelBinder
which will bind model from HttpContext.Items
您可以为此使用
HttpContext.Items
并创建HttpContextItemsModelBinder
它将从HttpContext.Items
绑定 model
public class HttpContextItemsModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var items = bindingContext.HttpContext.Items;
string name = bindingContext.BinderModelName ?? bindingContext.FieldName;
bindingContext.Result = items.TryGetValue(name, out object item)
? ModelBindingResult.Success(item)
: ModelBindingResult.Failed();
return Task.CompletedTask;
}
}
Create and register model binder provider创建并注册 model binder provider
public static class CustomBindingSources
{
public static BindingSource HttpContextItems { get; } = new BindingSource("HttpContextItems", "HttpContext Items", true, true);
}
public class HttpContextItemsModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.BindingInfo.BindingSource == CustomBindingSources.HttpContextItems)
{
return new HttpContextItemsModelBinder();
}
return null;
}
}
In Startup.cs
在
Startup.cs
中
services
.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new HttpContextItemsModelBinderProvider());
//...
})
Create an attribute which will set correct BindingSource
to use HttpContextItemsModelBinder
创建一个属性,该属性将设置正确的
BindingSource
以使用HttpContextItemsModelBinder
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FromHttpContextItemsAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
{
public string Name { get; set; }
public BindingSource BindingSource => CustomBindingSources.HttpContextItems;
public FromHttpContextItemsAttribute(string name)
{
Name = name;
}
public FromHttpContextItemsAttribute() { }
}
Usage:用法:
//in controller
[HttpGet]
[ValidateUserFilter]
public IActionResult TestHttpContextItems([FromHttpContextItems("UserItem")]UserItemModel model)
{
return Ok(model);
}
//your action filter
public class ValidateUserFilterAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public override void OnActionExecuting(ActionExecutingContext context)
{
//...
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var model = new UserItemModel
{
Id = 45,
Name = "Some user name"
};
context.HttpContext.Items["UserItem"] = model;
}
}
Important note重要的提示
Pay attention that I save user model to HttpContext.Items
during OnAuthorization
and not OnActionExecuting
because model binding happens before any action filters run, so HttpContext.Items
won't contain user and model binding will fail.请注意,我在
OnAuthorization
期间将用户 model 保存到HttpContext.Items
而不是OnActionExecuting
因为 model 绑定发生在任何操作过滤器运行之前,因此HttpContext.Items
不会包含用户并且 Z20F35E630DAF49D8CZ 绑定将失败。 You might need to adjust filter code to your needs and to make the solution work as expected.您可能需要根据需要调整过滤器代码并使解决方案按预期工作。
Usage without specifying item name.不指定项目名称的用法。 Parameter name in action method should match key (
"userModel"
) used to store value in HttpContext.Items
:操作方法中的参数名称应与用于在
HttpContext.Items
中存储值的键( "userModel"
)匹配:
//in controller
[HttpGet]
[ValidateUserFilter]
public IActionResult TestHttpContextItems([FromHttpContextItems]UserItemModel userModel)
{
return Ok(userModel);
}
//action filter
public class ValidateUserFilterAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public override void OnActionExecuting(ActionExecutingContext context)
{
//...
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//...
context.HttpContext.Items["userModel"] = model;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.