如何使用 Ninject 攔截來攔截所有 ASP.NET WebApi 控制器操作方法呼叫以進行日誌記錄?
每次呼叫我們的 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.有經驗的人可以指出我做錯了什麼嗎?謝謝。
更新
因此,使用您的程式碼和雷莫關於需要將操作方法設為虛擬並放入一個空的預設建構子(只是為了安撫動態代理,保持其他建構子靜止)的優秀觀點,我已經讓操作過濾器和攔截方法都工作了。
我想說,就目前而言,您的程式碼將攔截 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.]註釋掉攔截程式碼並使用動作過濾器,我得到了亞毫秒級的性能:
[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 的可能性?這是 AOP 在 MVC 操作上的自然擴展點。
如果您對控制器上的實際操作以外的方法感興趣,那麼我會理解,但我想我還是會發布一個建議。
靈感來自ActionFilterAttributes 是否跨執行緒重用?這是如何運作的?和測量呼叫 ASP.NET MVC 控制器操作的時間。
更新以顯示在方法標記時排除計時器。來自核心 WebApi 框架的靈感,特別是AllowAnonymousAttribute和AuthorizeAttribute
全域註冊這個,以便所有操作都被這個監控:
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() { // } }