18

我们公司有必要记录某些东西,每当我们的ASP.NET WebApi控制器的一个动作方法被调用时。由于我们现在使用Ninject作为DI,因此我们也希望将其用于此目的。这是我到目前为止所尝试的。如何拦截所有ASP.NET WebApi控制器操作方法调用与Ninject拦截记录?

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

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. 

可能有经验的人指出我做错了什么吗?谢谢。

+0

也许这个建议是设计中的一个太大的转变,但是当您将所有业务操作抽象为一个通用抽象并将其注入到控制器中时,您将能够轻松装饰或截取这些抽象以添加横切关注点如您的时间方面。看看[命令/处理程序模式](http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91)以了解我在说什么。 – Steven

+1

你有一个额外的无参数构造函数,并且所有的操作方法都是虚拟的吗? –

+1

谢谢雷莫,我添加虚拟所有的行动方法,但无参数构造函数我有一个问题,因为我们的服务对象被注入那里,并在控制器中使用。使用无参数构造函数时,服务对象为null。你如何解决这个问题?非常感谢! –

回答

26

更新

因此,使用你的代码和雷莫的有关需要的操作方法是虚拟的,并把一个空的默认构造良好的出发点(只是为了安抚动态代理,让您的其它构造仍)我已经有了动作过滤器和拦截方法。

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

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

[Execution of Get took 00:00:00.0046615.] 
[Execution of Get took 00:00:00.0041988.] 
[Execution of Get took 00:00:00.0039383.] 

注释掉拦截代码并使用动作过滤器我得到亚毫秒级的性能:

[Execution of Get took 00:00:00.0001146.] 
[Execution of Get took 00:00:00.0001116.] 
[Execution of Get took 00:00:00.0001364.] 

它是由你,这是否确实是一个问题或关注,但我我以为我会指出这一点。

先前响应

你有rulled了使用ActionFilters?这是MVC行动中AOP的自然延伸点。

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

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

已更新以显示在标记方法时排除计时器。

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 
{ 

} 

现在从核心的WebAPI框架专门AllowAnonymousAttributeAuthorizeAttribute

注册此全局,使所有操作都通过这个监控的启示您可以使用以下内容排除全局筛选器

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

+1嗨马克,我赞赏你的答案。但我的经理不想使用操作过滤器属性。他希望所有的操作方法都被记录下来,除了我们标记属性[nolog]的那些。 –

+1

@ ray247,谢谢你让我提出建议。在一分钱内,我已经更新了使用这种方法显示NoLog的能力。我也会尽快用一些Ninject例子来更新答案。 –

+2

感谢一堆标记。我还没有试过你的代码,但我绝对同意你上面显示的代码。 GlobalConfiguration.Configuration.Filters.Add将使每个动作都发生,而NoLog属性会过滤出我们不想要的。一个同事已经拿出了一个HttpModule解决方案来拦截每个请求。但是你的MVC项目更自然。谢谢你,新年快乐。标记为答案! –

2

对任何人来说仍然潜伏,我想用Ninject是这样我就可以注入记录器(或其他东西)进入拦截器,但我想拦截所有行动的原因。

马克的回答是完美的,但不是全球注册使用

Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action). 

你需要创建在TimingActionFilter类的适当构造器使用

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

与Ninject您的过滤器绑定。