带有备用字段的MVC / WebAPI ActionFilterAttributes

时间:2015-08-03 18:04:15

标签: asp.net-mvc asp.net-mvc-4 asp.net-web-api actionfilterattribute custom-action-filter

我想创建一个简单的全局ActionFilterAttribute,它会告诉我所请求的资源以及运行所需的时间。这是迄今为止的代码:

public class APITraceAttribute : ActionFilterAttribute
{
    private readonly Stopwatch timer;

    public APITraceAttribute()
    {
        timer = new Stopwatch();
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        timer.Restart();
        base.OnActionExecuting(actionContext);
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        timer.Stop();
        trace(actionExecutedContext.ActionContext, timer.Elapsed);
        base.OnActionExecuted(actionExecutedContext);
    }

    private static void trace(HttpActionContext actionContext, TimeSpan duration)
    {
        HttpMethod method = actionContext.Request.Method;
        string path = actionContext.Request.RequestUri.PathAndQuery;
        string controllerName = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
        string actionName = actionContext.ActionDescriptor.ActionName;
        double totalSeconds = duration.TotalSeconds;

        ILogger logger = new Logger("trace");
        logger.Trace("{0} {1}: {2}Controller.{3} ({4:N4} seconds)...", method, path, controllerName, actionName, totalSeconds);
    }
}

正如您所看到的,我使用Stopwatch作为支持字段,我在行动开始时Restart,在行动结束时使用Stop。我不需要很高的精度,所以这很好。

但是,我通过编辑/App_Start/WebApiConfig.cs文件来连接所有请求:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Filters.Add(new APITraceAttribute());
    }
}

我注意到这需要一个构造属性。这使我担心它将全面重用相同的属性实例,并且两个并发处理请求可能会尝试使用相同的Stopwatch实例。是这样的吗?如何确保每个请求都有自己的Stopwatch实例?

1 个答案:

答案 0 :(得分:1)

您可以使用HttpContext.Current.Items我认为可以安全地说它会按照请求存储。

试试这个

public override void OnActionExecuting(HttpActionContext actionContext)
{
    var timer = HttpContext.Current.Items["timer"] as Stopwatch;
    if (timer == null)
    {
       timer = new Stopwatch();
       HttpContext.Current.Items["timer"] = timer;
    }
    timer.Restart();
    base.OnActionExecuting(actionContext);
}

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    var timer = HttpContext.Current.Items["timer"] as Stopwatch;
    timer.Stop();
    trace(actionExecutedContext.ActionContext, timer.Elapsed);
    base.OnActionExecuted(actionExecutedContext);
}

我正在OnActionExecuting中创建Stopwatch对象,因为正如你所说的那样,过滤器的实例可以在请求之间共享,并且在构造函数中创建它是不安全的,所以每次OnActionExecuting触发它时都会使用HttpContext.Current wich请求独立。