zoukankan      html  css  js  c++  java
  • MVC请求管道详细总结梳理。

    MVC请求管道

    一、请求到达IIS之前是这样的:

     二、到达IIS之后是这样的:

    Sys服务监听到有请求到达IIS,IIS会把这个请求转发给ISAPI,ISAPI即Internet Server Application Program Interface (互联网应用服务接口),是微软提供的一套面向Internet服务的API接口,它根据后缀判断需要把该请求转发给谁处理,.net的请求会转发到asp.net_ISAPI,它属于IIS,运行在IIS进程中的,它会用Pipeline的方式把请求转交给.Net Framework,它很像一个队列(先进先出),可以对请求进行排队处理,在请求处理完,会释放掉该请求占用的socket链接。

    三、请求到达应用程序:

    HTTPRuntime.ProcessRequest是整个应用程序的入口,这一步会接收上一步打包的HTTPWorkerRequest,完成HttpContext的初始化,调用HttpApplicaFactory工厂类创建HttpApplication(HttpApplication实现了IHttpHandler),HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例中有三个关键方法:
               1. HttpApplicationFactory._theApplicationFactory.EnsureInited()该方法检查HttpApplicationFactory是否被初始化(它是存放在一个栈里面的)如果没有,就通过HttpApplicationFactory.Init()进行初始化。在Init()中,先获取Global.asax文件的完整路径,然后调用CompileApplication()对Global.asax进行编译。
               2. HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.Global.asax中的Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。
               3. HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 该方法创建HttpApplication实例并进行初始化,调用System.Web.HttpApplication.InitInternal()方法。创建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asa文件,也就是说没有动态编译生成ASP.Global.asax类型,那就直接实例化HttpApplication。如果创建了ASP.Global.asax类型,那就对ASP.Global.asax进行实例化。

    图2.

     

     四、请求到达mvc

      当请求到达应用程序之后,第一站就是路由模块,MVC会根据RouteTable(一般都在RouteConfig中配置,在MVC第一次启动时,路由系统就会把我们注册的路由规则,写在Glocal.asax中的Application_Start事件中)中配置的路由模板去匹配当前的请求,获取对应的Controller和Action信息,具体的匹配过程是由UrlRoutingModule(System.Web.Routing.UrlRoutingModule)来实现的。

          当UrlRoutingModule在Route Table中找到一条匹配的路由规则时,就会为这条路由规则寻找对应的IRouteHandler(System.Web.Mvc.IRouteHandler)实例(默认是System.Web.MvcRouteHandler),根据这个RouteHandler最后获取一个IHttpHandler的实例(默认是System.Web.MvcHandler)。在处理完路由信息和匹配到之后,就来到了Controller。

          在MvcHandler中的ProcessRequest方法中就是ASP.NET MVC的生命周期,这个方法使用IControllerFactory的实例(默认是System.Web.Mvc.DefaultControllerFactory)来创建相应的Controller。

          创建完Controller之后,就会执行Controller的invokeAction()方法,找到合适的Action后,就是Model Binding(默认是System.Web.Mvc.DefaultModelBinder),它会从Http请求的参数中提取数据并实现类型转换,数据校验(例如是否必填,数据格式等)以及是否自动装配到Action方法的参数中。

           完成模型绑定之后就是执行Filter了,Filter也是一个可以铺开说的知识,这里先只简单的介绍一下,从下图看到第一个是Authentication Filter,它是MVC5中新增的一个Filter,它会先于Authorization Filter执行,目的是对访问用户的认证。在MVC5之前,认证和授权都是通过Authorization Filter来实现的。然后是Authorization Filter 这个主要是验证授权的,授权验证通过后执行的Action Filter,自定义Action Filter需继承ActionFilterAttribute,它分为两个方法,OnActionExecuting和OnActionExecuted分别在action执行前后执行,我们可以通过实现IActionFilter接口来实现你个性化的过滤机制。在Action Filter执行之后,就会执行我们的业务代码了。

           在执行完业务代码,会执行ActionResult,ActionResult也有一个Filter,自定义ActionResult Filter需继承ActionFilterAttribute它也分为两个方法:OnResultExecuting和OnResultExecuted,它是在动作结果被执行之前和之后运行。ActionResult就是把后台处理的用户请求结果返回。因此ViewResult、PartialViewResult、RedirectToRouteResult、RedirectResult、ContentResult、JsonResult、FileResult 和 EmptyResult就是具体的返回类型。

          最后还有一个Exception Filter,它只有在当Action执行发生未处理异常的时候执行OnException方法。自定义Exception Filter需要继承HandleErrorAttribute类,重写OnException方法。

          在ActionResult执行完成之后,就是View的初始化和渲染了,ViewResult最终是由View Engine通过调用IView的Render()方法来呈现View的,整个处理过程是由IViewEngine(System.Web.Mvc.IViewEngine)来实现的。ASP.Net MVC 默认提供WebForm(.aspx)和Razor(.cshtml)模板引擎。

    五、请求管道中的相关方法有如下:

    (1)BeginRequest: 开始处理请求
    
    (2)AuthenticateRequest授权验证请求,获取用户授权信息
    
    (3):PostAuthenticateRequest获取成功
    
    (4): AuthorizeRequest 授权,一般来检查用户是否获得权限
    
    (5):PostAuthorizeRequest:获得授权
    
    (6):ResolveRequestCache:获取页面缓存结果
    
     
    
    (7):PostResolveRequestCache 已获取缓存 当前请求映射到MvcHandler(pr): 创建控制器工厂 ,创建控制器,调用action执行,view→response
    
    //action Handler : PR()
    
    (8):PostMapRequestHandler 创建页面对象:创建 最终处理当前http请求的 Handler 实例: 第一从HttpContext中获取当前的PR Handler ,Create
    
    (9):PostAcquireRequestState 获取Session
    
    (10)PostAcquireRequestState 获得Session
    
    (11)PreRequestHandlerExecute:准备执行页面对象
    执行页面对象的ProcessRequest方法
    
    (12)PostRequestHandlerExecute 执行完页面对象了
    
    (13)ReleaseRequestState 释放请求状态
    
    (14)PostReleaseRequestState 已释放请求状态
    
    (15)UpdateRequestCache 更新缓存
    
    (16)PostUpdateRequestCache 已更新缓存
    
    (17)LogRequest 日志记录
    
    (18)PostLogRequest 已完成日志
    
    (19)EndRequest 完成、

    六、怎么实现请求管道中的方法:下面示例就是实现管道中的第9个事件,判断session是否存在事件。

    public class ValidateSessionHttpModule:IHttpModule
        {
            public void Dispose()
            {
                throw new NotImplementedException();
            }
    
            public void Init(HttpApplication context)
            {
                //第九个事件
               context.AcquireRequestState+=context_AcquireRequestState;
            }
            public void context_AcquireRequestState(object sender, EventArgs e)
            {
                HttpApplication application = sender as HttpApplication;
                HttpContext context=application.Context;//获取当前的HttpContext
               string url= context.Request.Url.ToString();//获取用户请求的URL地址。
               if (url.Contains("Admin"))
               {
                   if (context.Session["userInfo"] == null)
                   {
                       context.Response.Redirect("/Login.aspx");
                   }
               }
            }
        }

    上面这个只是第一步,下面是很重要的第二步:配置文件中添加system.webService节点。

      <!--关于HttpModule的配置-->
      <system.webServer>
        <modules>
          <add name="ValidateSessionHttpModule" type="CZBK.ItcastProject.Common.ValidateSessionHttpModule"/>
        </modules>
      </system.webServer>

    以上代码是什么意思呢?意思就是说,当加载管道事件的时候,会读取管道中的system.webService所有的配置项,并且便利加载注入在管道中。然后当请求到对应步骤的时候,就会进行应用进去。

    还有一种注册管道中事件的方法,估计大家都没有注意到,哪就是我们的global.aspx文件。

    public class Global : System.Web.HttpApplication
        {
            /// <summary>
            /// Web应用程序第一次启动时调用该方法,并且该方法只被调用一次。
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            protected void Application_Start(object sender, EventArgs e)
            {
               
            }
            /// <summary>
            /// 开始会话。(用户通过浏览器第一次访问我们网站中的某个页面,这时建立会话,但是当该用户通过浏览器再次访问其它的页面时,该方法不会被执行。SessionId)
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            protected void Session_Start(object sender, EventArgs e)
            {// Application:服务端的状态保持机制。放在该对象中的数据是共享的。 Cache
                Application.Lock();
                int count = Convert.ToInt32(Application["count"]);
                count++;
                Application["count"] = count;
                Application.UnLock();
            }
            protected void Application_BeginRequest(object sender, EventArgs e)
            {
    
            }
    
            protected void Application_AuthenticateRequest(object sender, EventArgs e)
            {
    
            }
    
            
            /// <summary>
            /// 全局的异常处理点。
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            protected void Application_Error(object sender, EventArgs e)
            {
                //Exception ex = HttpContext.Current.Server.GetLastError();
                ///ex.Message
                ///写到日志中。
                    //Log4Net
            }
            /// <summary>
            /// 会话结束
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            protected void Session_End(object sender, EventArgs e)
            {
                Application.Lock();
                int count = Convert.ToInt32(Application["count"]);
                count--;
                Application["count"] = count;
                Application.UnLock();
            }
    
    
            protected void Application_End(object sender, EventArgs e)
            {
    
            }
        }

    谢谢学习,以上就是本次总结的基本内容,其中部分内容摘自:https://www.cnblogs.com/bobo-pcb/p/11717289.html

    欢迎分享学习!!!

  • 相关阅读:
    Spring Boot2 系列教程(二十)Spring Boot 整合JdbcTemplate 多数据源
    Spring Boot 如何给微信公众号返回消息
    Spring Boot2 系列教程(十九)Spring Boot 整合 JdbcTemplate
    Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
    Spring Boot 开发微信公众号后台
    Spring Boot2 系列教程(十七)SpringBoot 整合 Swagger2
    Spring Boot2 系列教程(十六)定时任务的两种实现方式
    Spring Boot2 系列教程(十五)定义系统启动任务的两种方式
    Spring Boot2 系列教程(十四)CORS 解决跨域问题
    JavaScript二维数组
  • 原文地址:https://www.cnblogs.com/wangjinya/p/13733234.html
Copyright © 2011-2022 走看看