在ASP.NET Core中,从ExceptionFilterAttribute访问控制器

11

如何在ASP.Net Core 2中获取应用于ExceptionFilterAttribute的Controller实例?

现在在Core中,是否有更好的方式来实现共享“基本”控制器属性和方法等,比如将其放在更高的级别,如Startup中?

在Core之前,在MVC 4中,我会这样做:

/// <summary>
/// Base controller, shared by all WebAPI controllers for tracking and shared properties.
/// </summary>
[ApiTracking]
[ApiException]
public class BaseApiController : ApiController
{
    private Common.Models.Tracking _Tracking = null;
    public Common.Models.Tracking Tracking
    {
        get
        {
           if(_Tracking == null)
               _Tracking = new Common.Models.Tracking();
           return _Tracking;
        }
    }
    //... other shared properties...
}

/// <summary>
/// Error handler for webapi calls. Adds tracking to base controller.
/// </summary>
public class ApiExceptionAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext cntxt)
    {
        BaseApiController ctrl = cntxt.ActionContext.ControllerContext.Controller as BaseApiController;
        if (ctrl != null)
            ctrl.Tracking.Exception(cntxt.Exception, true);

        base.OnException(actionExecutedContext);
    }
}

/// <summary>
/// Intercepts all requests to inject tracking detail and call tracking.save.
/// </summary>
public class ApiTrackingAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext cntxt)
    {
        //...add info to tracking
    }
    public override void OnActionExecuted(HttpActionExecutedContext cntxt)
    {
        BaseApiController ctrl = cntxt.ActionContext.ControllerContext.Controller as BaseApiController;
        if (ctrl != null)
            ctrl.Tracking.Save();
    }
}

1
你其实不必使用基础控制器来将属性应用于所有控制器。在核心中有所不同,但仍然可以在没有基础控制器的情况下应用属性。参考链接 - Erik Philips
@ErikPhilips,您提供的链接展示了如何添加全局过滤器,但问题在于我们如何在过滤器中获取控制器上下文。 - programtreasures
@programtreasures 我明白你的意思。这就是为什么我的信息是一条评论而不是答案。 - Erik Philips
2个回答

3

HttpContext 在 ASP.Net Core 中包含一个 IDictionary<object, object> 类型的 Items 属性,用于在请求范围内共享数据。这正是您需要覆盖您的情况所需的。以下是一个示例实现:

public class ApiExceptionAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var items = context.HttpContext.Items;
        if (items.ContainsKey("Tracking"))
        {
            Tracking tracking = (Tracking)items["Tracking"];
            tracking.Exception(context.Exception, true);
        }

        base.OnException(context);
    }
}

public class ApiTrackingAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var tracking = new Tracking();
        //...add info to tracking

        context.HttpContext.Items.Add("Tracking", tracking);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var items = context.HttpContext.Items;
        if (items.ContainsKey("Tracking"))
        {
            Tracking tracking = (Tracking) items["Tracking"];
            tracking.Save();
        }
    }
}

1
通过使用HttpContext.Items来获取属性确实很好,但是我正在寻找控制器上下文,我们如何从ExceptionContext中找到ControllerContext呢? - programtreasures
1
之前在ActionContext中有一个ControllerContext属性,ExceptionContext是从这个属性继承而来的。但是它已经被移除了,请查看GitHub上的这个问题获取详细信息。我的回答涵盖了原始问题,OP不需要使用ControllerContext来实现他的目标。 - CodeFuller
谢谢@CodeFuller。我会尽快测试这个。我一直使用基础控制器的原因之一是,在派生控制器中,我还会将this.Tracking传递到帮助程序调用等中,因此跟踪主要由帮助程序填充。使用context.items应该可以解决问题。 - ZAC
@CodeFuller,谢谢你的帮助,这确实解决了我在过滤器中访问事物的问题。但是,我还是有些困扰,因为过滤器执行是无序的...所以在我的情况下,跟踪操作会在异常被捕获之前进行保存。我会在这个主题之外找到解决方法的。 - ZAC

0

如果您正在使用.NET 6或更高版本,请查看我的答案此处。我提供了一个示例,演示如何使用Microsoft.AspNetCore.Mvc.Filters.IActionFilter和Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter组合来访问控制器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接