asp.net-mvc - 如何访问 ActionFilter 中的属性?
问题描述
这是我的动作过滤器:
public class LogAttribute : ActionFilterAttribute
{
public LogAttribute(ILoggerFactory logger)
{
Logger = logger.CreateLogger<LogAttribute>();
}
public string UserId { get; set; }
public string Path { get; set; }
public ILogger Logger { get; }
public override void OnActionExecuting(ActionExecutingContext context)
{
var controller = context.Controller as Controller;
UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
Path = $"{context.HttpContext.Request.Path.Value}[{context.HttpContext.Request.Method}]";
Logger.LogDebug($"{Path} started. User(id): {UserId}.");
}
}
我想在我的控制器的其他地方使用 UserId 和 Path 属性。有简洁的方法吗?
解决方案
我同意 Serge 的观点,最好使用 BaseController 来完成这项任务。没有办法用属性清楚地做到这一点。
例如:
public abstract class BaseController<T> : Controller where T : ControllerBase
{
protected readonly ILogger<T> Logger;
protected BaseController(ILogger<T> logger)
{
Logger = logger;
}
protected string UserId { get; set; }
protected string Path { get; set; }
[NonAction]
public override void OnActionExecuting(ActionExecutingContext context)
{
UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
Path = $"{context.HttpContext.Request.Path.Value}[{context.HttpContext.Request.Method}]";
Logger.LogDebug($"{Path} started. User(id): {UserId}.");
}
}
public class HomeController : BaseController<HomeController>
{
public HomeController(ILogger<HomeController> logger): base(logger)
{
}
}
或者只是在您的特定控制器中使用覆盖 OnActionExecuting
public class HomeController: Controller
{
private readonly ILogger<HomeController> _logger;
private string _userId;
private string _path;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
[NonAction]
public override void OnActionExecuting(ActionExecutingContext context)
{
_userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
_path = $"{context.HttpContext.Request.Path.Value}
[{context.HttpContext.Request.Method}]";
_logger.LogDebug($"{Path} started. User(id): {UserId}.");
}
}
我将向您展示为此使用属性的一些缺点。
1.您可以使用HttpContext.Items实现IDictionary<object,object>并且可用于请求(生命周期作为范围)。但为此,您必须使用资源过滤器。
[AttributeUsage(AttributeTargets.Class)]
public class SampleLogAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.HttpContext.Items["path"] = $"{context.HttpContext.Request.Path}[{context.HttpContext.Request.Method}]";
context.HttpContext.Items["userId"] = "SomeUserIdFromResource";
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
然后你可以使用 OnActionExecuting 从你的控制器获取它
private string _path;
private string _userId;
public override void OnActionExecuting(ActionExecutingContext context)
{
_path = HttpContext.Items["path"] as string;
_userId = HttpContext.Items["userId"] as string;
}
不这样做的主要原因 - 您必须知道您的过滤器将值设置为Items并始终在控制器中创建它们。
2.您可以在属性中使用反射设置私有值
public override void OnActionExecuting(ActionExecutingContext context)
{
var controller = context.Controller;
var path = controller.GetType().GetField("_path", BindingFlags.NonPublic | BindingFlags.Instance);
path?.SetValue(controller, $"{context.HttpContext.Request.Path}[{context.HttpContext.Request.Method}]");
var userId = controller.GetType().GetField("_userId", BindingFlags.NonPublic | BindingFlags.Instance);
userId?.SetValue(controller, "SomeUserId");
}
如果您忘记创建基本变量,您必须了解 - 您无法访问它们。
推荐阅读
- sql-server - 我可以在单台电脑上创建额外的 SQL Server 以使用 SQL Server 身份验证而不是 Windows 身份验证进行测试吗?
- angular - Angular Chartjs 错误 TS2304:找不到名称 OffscreenCanvasRenderingContext2D
- sql - 比较列和重复数据删除
- java - 通过子字符串在 selenium 中查找元素
- testing - 您可以使用 cypress 测试 Teams 应用程序吗?
- plot - Pine Script 中的抵消地块价格交叉地块价格
- python - 正则表达式负前瞻(python)
- javascript - 从 localhost Vue 项目调用 api 时出现 NodeJS CORS 错误
- laravel - Livewire 电线:点击,电线:模型不工作
- swift - 所有内置 xcode 的应用程序都因“终止原因:命名空间代码设计,代码 0x1”而崩溃