ASP.NET MVC 生命周期 - 每个页面请求做一些工作

Posted

技术标签:

【中文标题】ASP.NET MVC 生命周期 - 每个页面请求做一些工作【英文标题】:ASP.NET MVC Lifecycle - Do Some Work Once Per Page Request 【发布时间】:2015-01-21 23:04:29 【问题描述】:

我想针对 .NET MVC Web 应用程序的每个 Web 请求执行一些工作。具体来说,我想为每个页面加载在数据库中创建一个日志条目。

在重写的 Controller 类 Controller.Initialize() 方法中执行此工作不起作用,因为 new controller is created with every call to @html.Action()。因此,如果从视图调用子操作,那么我最终会出现双重日志记录——这不是我想要的。

如何在 MVC 生命周期中插入一些工作,以便每个页面请求执行一次?

【问题讨论】:

在 global.asax 中尝试BeginRequest 事件 @VsevolodGoloviznin Try BeginRequest event in global.asax BeginRequest 事件也会在单个页面加载中被多次调用。 Vsevolod,你的意思是Application_BeginRequest吗?我刚试过。不,不是每个网络请求都调用该方法。 请记住,MVC 中没有页面之类的东西。您应该根据控制器操作重新制定您的要求。然后,您必须明确要记录哪些控制器操作,哪些不记录。 @JohnSaunders,是的,标题可能是“根据 Web 请求做一些工作”。 【参考方案1】:

您可以使用 OnActionExecutingOnActionExecuted

例如,

public class HomeController : Controller

    [LogActionFilter]
    public ActionResult Index()
    
        return View();
    


public class LogActionFilterAttribute : ActionFilterAttribute

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    
        // If you register globally, you need this check.
        if (!filterContext.IsChildAction)
        
           Log("OnActionExecuting", filterContext.RouteData);
        
        base.OnActionExecuting(filterContext);
    

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    
        // If you register globally, you need this check.
        if (!filterContext.IsChildAction)
        
           Log("OnActionExecuted", filterContext.RouteData);
        
        base.OnActionExecuted(filterContext);
    

    private void Log(string methodName, RouteData routeData)
    
        var controllerName = routeData.Values["controller"];
        var actionName = routeData.Values["action"];
        var message = String.Format("0 controller:1 action:2", methodName, controllerName, actionName);
        Debug.WriteLine(message, "Action Filter Log");
    

【讨论】:

嗨@Win,这可能有效;我曾考虑创建一个新的 ActionFilterAttribute。但我相信我必须对许多现有的控制器操作进行装饰(应用这个新属性)。对吗? 是的。理想情况下,您只需将 LogActionFilter 添加到重要的 Action 方法中。换句话说,您不想记录每个操作方法,因为记录成本很高。 @Michael,您不必将属性应用到每个控制器,您可以全局注册。 @Michael Craig 是正确的。如果您在全球范围内注册,请确保选中 IsChildAction。我更新了答案。 @Win,创建一个新的ActionFilterAttribute 看起来也是一个不错的解决方案。 :) @Craig W. 我不知道我可以在全球范围内注册它。感谢您的帮助!【参考方案2】:

如果操作是子操作,您可以签入Initialize

ControllerContext.IsChildAction

【讨论】:

尝试protected override void Initialize() 中的if (!ControllerContext.IsChildAction) sn-p 不起作用,因为ControllerContext 为空。 Romias,我再次探讨了您的建议。我注意到ControllerContext.IsChildAction protected override void OnActionExecuted() 中可用。 而不是在 Initialize... 中创建一个 GlobalFilter,并在 Global.asax 中注册它 OnActionExecuted 在开始渲染视图之前以及将模型传递给视图(模型)之后触发。所以在那一刻你可以记录模型本身的信息(例如序列化)。 谢谢。在protected override void OnActionExecuted() 中使用ControllerContext.IsChildAction 对我有用。

以上是关于ASP.NET MVC 生命周期 - 每个页面请求做一些工作的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET MVC 控制器生命周期

Asp.net MVC 之请求生命周期

asp.net mvc 生命周期

asp.net MVC 应用程序的生命周期

ASP.NET MVC5请求管道和生命周期

ASP.NET MVC5请求管道和生命周期