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

时间:2012-12-27 22:17:56

标签: asp.net-mvc logging asp.net-web-api ninject ninject-interception

每次调用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());
    }
}

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();
    }
}

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

var kernel = new StandardKernel(new InterceptAllModule());

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

Cannot instantiate proxy of class: MyApiController.

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

2 个答案:

答案 0 :(得分:29)

<强>更新

所以使用你的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.]

注释掉拦截代码并使用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.]

这取决于你是否实际上是一个问题或关注点,但我想我会指出这一点。

以前的回复

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

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

Are ActionFilterAttributes reused across threads? How does that work?Measure Time Invoking ASP.NET MVC Controller Actions的启发。

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

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

GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());

然后:

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();
    }
}

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

}

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

public class ExampleController : ApiController
{
    // GET api/example
    [NoLog]
    public Example Get()
    {
       //
    }
}

答案 1 :(得分:2)

对于仍然潜伏的人,我想使用Ninject的原因是我可以将一个记录器(或其他任何东西)注入拦截器,但我想拦截所有动作。

马克的答案是完美的,但不是使用

进行全球注册
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());

使用

将过滤器与Ninject绑定
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).

您需要在TimingActionFilter类中创建适当的构造函数。