每次调用我们的 ASP.NET WebApi Controller 的一种操作方法时,我们公司都需要记录某些事情。由于我们现在将 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.
有经验的人可以指出我做错了什么吗?谢谢。
最佳答案
更新
因此,使用您的代码和 Remo 关于需要将操作方法设为虚拟并放入一个空的默认构造函数(只是为了安抚动态代理, 保持您的其他构造函数仍然为 )的优秀观点,我已经得到了操作过滤器和拦截方法有效。
我会说,就目前而言,您的代码将拦截 ApiController 上可能不需要的方法,因此您可能还需要放置一些代码来过滤掉这些方法,例如执行异步和处置。
我唯一的另一点是性能。 巨大的免责声明这些只是非常基本的测试(每次都使用 Action 过滤器方法来记录统计信息),我邀请您自己做(!)...但是使用 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 的可能性?这是 AOP 在 MVC 操作上的自然扩展点。
如果您对 Controller 上的实际操作以外的方法感兴趣,那么我会理解,但我想我还是会发布一个建议。
灵感来自 Are ActionFilterAttributes reused across threads? How does that work?和 Measure Time Invoking ASP.NET MVC Controller Actions .
更新以显示在方法标记时排除计时器。来自核心 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()
{
//
}
}
关于asp.net-mvc - 如何使用 Ninject 拦截来拦截所有 ASP.NET WebApi Controller 操作方法调用以进行日志记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14062028/