ASP.NET MVC3控制器AOP代理没有拦截所有方法,只有IController.Execute

本文关键字:有方法 只有 Execute IController MVC3 NET 控制器 AOP 代理 ASP | 更新日期: 2023-09-27 18:20:48

我有一个包含多个层的项目,其中包括web前端(ASP.NET MVC3)和服务后端(主要是业务逻辑)。这个项目才几个月,所以一切都如预期的那样进行。现在,我正尝试使用自定义[Log]属性为一些MVC3控制器方法添加日志记录方面。

我正在使用Castle Windsor进行依赖项注入。为了获得日志方面,我通过SNAP利用Castle DynamicProxy。控制器正在使用Krzysztof Koßmic的有用教程中的WindsorControllerFactory进行解析,但我对其进行了修改,以寻找控制器的默认接口(见下文)。

在我的服务层:

[Log(LoggingLevel.Info)]
public void Save(MyBusinessDto dto)
{
    // business logic and other checks
    this.repository.Save(mbo);
}

在我的网络前端的控制器IWindsorInstaller中:

private static BasedOnDescriptor FindControllers()
{
    return AllTypes
            .FromThisAssembly()
            .BasedOn<IController>()
            .WithService.DefaultInterface();
}

在我的(稍微定制的)WindsorControllerFactory中,它为控制器寻找默认接口:

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        throw new HttpException(404, string.Format(Error404, requestContext.HttpContext.Request.Path));
    }
    string controllerName = controllerType.Name;
    string defaultInterfaceName = 'I' + controllerName;
    Type defaultInterface = controllerType.GetInterface(defaultInterfaceName);
    object controller = this.kernel.Resolve(defaultInterface);
    return (IController)controller;
}

在我的控制器中:

public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
    [Log(LoggingLevel.Debug)]
    public ActionResult CreateOrUpdate(MyBusinessFormModel fm)
    {
        // Convert form model to data transfer object,
        // perform validation and other checks
        this.service.Save(dto);
        return View(fm);
    }
}

这一切在服务项目中都很好,但在控制器中,方法不会被拦截。

  • 我已经确认WindsorControllerFactory返回代理的控制器
  • 我已经确认控制器已经注册了拦截器
  • 我已经确认SNAP中的MasterProxy拦截了控制器,但它只拦截了IController.Execute(RequestContext requestContext)

如何拦截所有具有[Log]属性的控制器方法

更新1:我曾考虑直接使用DynamicProxy而不是SNAP,但这对于让它也适用于控制器来说是次要的。

更新2+4:看来SNAP在github上的github中丢失了。

更新3:这是我在打开WindsorControllerFactory时在VisualStudio调试器中看到的内容(请参见上文)。被检查的controller变量就是返回给MVC的变量,并且它确实是代理的。

  • controller{Castle.Proxies.IMyBusinessControllerProxy}
    • __interceptors{Castle.DynamicProxy.IInterceptor[1]}
      • [0]{Snap.MasterProxy}
    • __target{My.Business.Web.Controllers.MyBusinessController}
      • service{Castle.Proxies.IMyBusinessServiceProxy}
      • (其他拮抗剂注射)
    • MyInjectedProperty{我的业务有用的MyOtherType}

ASP.NET MVC3控制器AOP代理没有拦截所有方法,只有IController.Execute

IController GetControllerInstance(...)中,不提供接口代理,而是使用virtual方法提供类代理

IController GetControllerInstance(...)返回的控制器中用户实现的方法不会通过代理的IMyBusinessController接口访问,而是从IController转换到控制器的实际类;例如CCD_ 22。请改用类代理,以使MVC3的强制转换返回代理。此外,请将方法标记为virtual,否则拦截代理将无法拦截方法调用并检查自定义属性。

在控制器中,将virtual添加到具有属性的方法中:

public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
    [Log(LoggingLevel.Debug)]
    public virtual ActionResult CreateOrUpdate(MyBusinessFormModel fm)
    {
        // Convert form model to data transfer object,
        // perform validation and other checks
        this.service.Save(dto);
        return View(fm);
    }
}

为什么只有Execute(...)被拦截IController接口仅包含Execute(...)。Execute是在返回的控制器接口代理上调用的,因此它可以被拦截。但是,一旦MVC3的内部ControllerBase.Execute(...)得到调用,它就会执行从ControllerFactory到它所期望的类的强制转换。

该问题类似于this泄漏,因为两者都绕过了接口代理。我想它可以通过多种方式解决;也许通过创建一个自定义类型转换器,在工厂中从接口代理的目标创建一个类代理,一个聪明的Windsor配置等等。

Krzysztof Koómic的IController安装程序和WindsorControllerFactory应该开箱即用。从更大的角度来看,可能会推荐接口代理(在控制器中使用拦截器之前,它们运行良好),但在这种情况下,可能有理由不走那么远,以避免进一步的副作用。

感谢马吕斯为我指明了正确的方向!

由于DynamicProxy(SNAP使用DynamicProxy)无法拦截非虚拟方法,我猜测返回的代理是控制器的派生类,因此,非虚拟方法将被忽略。您要么需要使SNAP(但不知道这是如何工作的)返回一个带有目标(您的实现)的接口代理,要么只是尝试使您的控制器方法虚拟化。