如何拦截所有ASP.NET WebApi控制器操作方法调用Ninject拦截进行日志记录?

Ray*_*Ray 20 asp.net-mvc logging ninject ninject-interception asp.net-web-api

每次调用ASP.NET WebApi控制器的一个操作方法时,我们公司都需要记录某些内容.由于我们现在使用Ninject作为DI,我们也想将它用于此目的.这是我到目前为止所尝试的.

我有通过NuGet安装的Ninject,Ninject.Extensions.Interception和Ninject.Extensions.Interception.DynamicProxy,我有以下模块

public class InterceptAllModule : InterceptionModule
{
    public override void Load()
    {
        Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
    }
}
Run Code Online (Sandbox Code Playgroud)

TimingInterceptor的位置

public class TimingInterceptor : SimpleInterceptor
{
    readonly Stopwatch _stopwatch = new Stopwatch();
    protected override void BeforeInvoke(IInvocation invocation)
    {
        _stopwatch.Start();
    }

    protected override void AfterInvoke(IInvocation invocation)
    {
        _stopwatch.Stop();
        string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
        Log.Info(message + "\n");
        _stopwatch.Reset();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,当我尝试使用ninject内核挂起模块时,运行我的站点

var kernel = new StandardKernel(new InterceptAllModule());
Run Code Online (Sandbox Code Playgroud)

但是,只要有一个调用进入其中一个操作方法,它就会抛出一个错误说

Cannot instantiate proxy of class: MyApiController.
Run Code Online (Sandbox Code Playgroud)

有经验的人可以指出我做错了吗?谢谢.

Mar*_*nes 30

更新

因此,使用你的Code和Remo关于需要将操作方法​​设置为虚拟并放入一个空的默认构造函数(只是为了安抚动态代理,保持其他构造函数仍然存在)的优点,我得到了动作过滤器和拦截方法.

我会说,因为它代表你的代码将拦截ApiController上可能不需要的方法,所以你可能还需要放置一些代码来过滤这些,例如ExecuteAsync和Dispose.

我唯一的另一点是表现.巨大的免责声明这些只是非常基本的测试(每次使用动作过滤器方法记录统计数据),我邀请你做自己的(!)...但是使用DynamicProxy拦截器我得到的时间大约是4毫秒得到请求

[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Run Code Online (Sandbox Code Playgroud)

注释掉拦截代码并使用Action过滤器我获得了亚毫秒的性能:

[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
Run Code Online (Sandbox Code Playgroud)

这取决于你是否真的是一个问题或关注,但我想我会指出这一点.

以前的回应

你有没有使用ActionFilters?这是MVC操作上AOP的自然扩展点.

如果您对控制器上的实际操作以外的其他方法感兴趣,那么我会理解,但我认为无论如何我都会发布一个建议.

灵感来自ActionFilterAttributes是否跨线程重复使用?这是如何运作的?测量时间调用ASP.NET MVC控制器操作.

更新以显示方法标记时排除计时器.核心WebApi框架的灵感来自AllowAnonymousAttributeAuthorizeAttribute

全局注册,以便通过以下方式监控所有操作:

GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Run Code Online (Sandbox Code Playgroud)

然后:

public class TimingActionFilter : ActionFilterAttribute
{
    private const string Key = "__action_duration__";

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (SkipLogging(actionContext))
        {
            return;
        }

        var stopWatch = new Stopwatch();
        actionContext.Request.Properties[Key] = stopWatch;
        stopWatch.Start();
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
        {
            return;
        }

        var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
        if(stopWatch != null)
        {
            stopWatch.Stop();
            var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
            Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
        }

    }

    private static bool SkipLogging(HttpActionContext actionContext)
    {
        return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
                actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
    }
}
Run Code Online (Sandbox Code Playgroud)

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{

}
Run Code Online (Sandbox Code Playgroud)

现在您可以使用以下方法排除全局过滤器

public class ExampleController : ApiController
{
    // GET api/example
    [NoLog]
    public Example Get()
    {
       //
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢一群马克.我还没有尝试过您的代码,但我完全同意您在上面显示的代码.GlobalConfiguration.Configuration.Filters.Add将使每个动作都成功,NoLog属性将过滤掉我们不想要的动作.一位同事带来了HttpModule的解决方案,可以拦截每个请求.但你的MVC项目更自然.谢谢,祝你新年快乐.标记为答案! (2认同)