• (转)ASP.NET架构分析


    最近听了微软讲师邵志东的讲座“深入解析ASP.NET架构”的视频讲座,我觉得对ASP.NET架构有一定的认识,现在对讲座做一点总结,以及发表我对ASP.NET架构相关知识的自身理解,如有不妥之处,希望各位同仁不吝指出!
    一、ASP.NET工作原理:
    首先通过一个图来了解一下ASP.NET工作原理,
      
     
                                         图一 ASP.NET工作原理
    通过该图的顺序我们可以看到ASP.NET是如何工作的,可以看到客户端和服务器端是如何交互的。看到客户端是如何请求服务器,服务器接受到客户端请求后,是如何处理请求的,并将处理结果返回给客户端的。
    需要指出的是图中的aspnet_isapi.dll是用来处理.aspx文件的,其实IIS服务器是只能识别.html文件的,当浏览器对服务器进行http请求时,IIS服务器识别到.aspx文件时,IIS服务器将其交给aspnet_isapi.dll来处理,aspnet_isapi.dll将.aspx文件发送给.NET FrameWork中CLR(公共语言运行时)进行编译,编译后将HTML流返回给浏览器。
    这在IIS服务器上是可以来配置的,配置中默认.aspx文件是由aspnet_isapi.dll来处理的,其实也可以去添加某种特定类型的文件由特定的可执行文件来处理,如:可以添加一个.apx文件来由aspnet_isapi.dll来处理。
    具体配置如下图:

                                    图二 IIS配置

    趁此说一下IIS服务的一些知识:
    IIS本事是不支持动态页面的,也就是说它仅仅支持静态html页面的内容,对于如.aspx,.php,.cs等,IIS并不会处理这些标记,它就会把它当作文本,丝毫不做处理发送到客户端。为了解决这个问题。IIS有一种机制,叫做ISAPI的筛选器,它是一个标准组件(COM组件。)  ASP.NET服务在注册到IIS的时候,会把每个扩展可以处理的文件扩展名注册到IIS里面(如:*.aspx,*.php等),扩展启动后,就根据定义好的方式来处理IIS所不能处理的文件,然后把控制权跳转到专门处理代码的进程中(如*.aspx由aspnet_isapi.dll来处理)。让这个进程开始处理代码,生成标准的HTML代码,生成后把这些代码加入到原有的HTML代码中,最后把完整的HTML返回到IIS,IIS再把内容发送给客户端。


    通过下图可以看到更具体的ASP.NET页面的执行过程:


                                     图三 ASP.NET页面的执行过程


    二、ASP.NET请求的处理过程:

    上面我们已经了解了ASP.NET的工作原理,知道了客户端是如何和服务器端进行交互的。现在我们可以来看看服务器端到底是怎样来处理客户端的请求。
    Web服务器提供了很多处理请求的功能,但是为了满足开发者的需求,开发者需要扩充或扩展Web服务器的功能,就是向Web服务器插入某些组件来增强Web服务器的功能,微软公司提供了ISAPI(Internet服务器API)。就比如说aspnet_isapi.dll可以来处理.aspx等文件,我们也可以开发组件(如xxx.dll)来处理某种类型的文件或者去做一些其他增强服务器能力的事情。

    简单的说如何去增强Web服务器的能力呢,我们就可以去开发一些组件,开发组件需要一种技术叫ISAPI。
    (ISAPI是一种重要的技术,它允许我们增强与ISAPI兼容的Web服务器的能力,其中IIS就是一种与ISAPI兼容的Web服务器。)

    增强Web服务器的能力的组件的类型主要有两种:
    1、ISAPI过滤器:客户端每次向服务器发出请求的时候,请求要经过过滤器。客户端不需要在请求中指定过滤器,只需要简单地把请求发送给Web服务器,接着Web服务器把请求传递给相关的过滤器。接下来过滤器可能修改请求,执行某些登录操作等。也就是说,客户端每次请求服务器时,必须要经过“过滤”,如需要检查用户名和密码,符合条件时才让通过请求,“检查用户名和密码”的过程就是ISAPI过滤器所要做的操作。过滤器可以有很多(web.config中指定,后面将讲到),并且请求都必须要经过所有的过滤器进行“过滤”。

    2、ISAPI扩展:ISAPI扩展是使用Win32动态链接库(xxx.dll)来实现的,也可以把ISAPI扩展看做一个普通的应用程序。它的处理目标是http请求。也就是说Web服务器可以来处理http请求,但是你可以去扩展服务器,去自定义对http的请求,达到更好的效果。

    基于上面的内容,我们可以将ASP.NET请求的处理过程总结如下:

    ASP.NET请求处理过程是基于“管道模型”的,客户端向服务器发送http请求时,在模型中ASP.NET把http请求传递给管道中的所有模块(ISAPI过滤器),每个模块都接受http请求并且有完全控制权限,模块可以用任何自认为的方式(通过开发人员来开发,如校验用户名密码等)来处理请求。一旦请求经过了所有Http模块(如用户名、密码符合),就最终被Http处理程序(ISAPI扩展)处理,http处理程序对请求进行一些处理,并且结果将再次经过管道中的http模块。

                    图四  管道模型

    我自己举例来说吧,我建立了一个.apx文件,要求客户端访问该页面,并在客户端返回"我是apx文件,我是被“ISAPI过滤器”过滤过的,并被"ISAPI扩展"处理过的"。在访问页面时必须需要用户名和密码。
    此时我就需要开发组件来扩充Web服务器,因为Web服务器没有校验指定的用户名和密码的能力也没有处理.apx文件的能力。我就需要开发“ISAPI过滤器”组件(用来校验用户名和密码)和ISAPI扩展组件(处理.apx文件)。

    那我们怎么开发“ISAPI过滤器”组件和“ISAPI扩展”组件呢?

    1、开发“ISAPI过滤器”
       HttpModule(Http模块)实现了ISAPI 过滤器的功能,是通过对IhttpModule接口的继承来处理。HttpModule是实现了System.Web.IhttpModule接口的.NET组件,这些组件通过在某些事件中注册自身,把自己插入到ASP.NET请求处理管道中(如上图中的Module1,Module2)。当这些事件发生的时候,ASP.NET调用对请求有兴趣的HTTP模块,这样该模块就能处理请求了。

       HttpModule的实现:
       1、编写一个类,实现IhttpModule接口,要添加引用System.Web
       2、实现Init方法,并且要注册需要的方法。如:AuthenticateRequest等
       3、实现注册的方法
       4、实现Dispose方法,如果需要手工为类做一些清除工作,可以添加Dispose方法的实现,但这不是必需的,通常可以
         不为Dispose方法添加任何代码
       5、在Web.config文件中,注册您编写的类。
       
       下面通过一个实例来实现HttpModule(具体见附件代码)

    1、我编写了SecurityModules类实现IHttpModule接口
    2、实现了Init方法,并且向Application对象注册事件处理程序(myAuthenticateRequest),当http请求执行到AuthenticateRequest(建立用户标   识时)时,就执行注册的方法myAuthenticateRequest
    3、实现注册的方法myAuthenticateRequest,主要来校验userid和password
    4、实现Dispose方法

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Text;
     4using
     System.Web;
     5using
     System.Security.Principal;
     6

     7namespace
     MyModule1
     8
    {
     9    public class
     SecurityModules : IHttpModule
    10    
    {
    11        public void
     Init(HttpApplication objApplication)
    12        
    {
    13            // 向Application 对象的事件AuthenticateRequest注册处理程序

    14            objApplication.AuthenticateRequest += new EventHandler(this.myAuthenticateRequest);
    15        }

    16
    17        private void myAuthenticateRequest(object
     objSender, EventArgs objEventArgs)
    18        
    {
    19            // 鉴别用户的凭证,并找出用户角色

    20            HttpApplication objApp = (HttpApplication)objSender;
    21            HttpContext objContext =
     (HttpContext)objApp.Context;
    22            if ((objContext.Request["userid"== null&& (objContext.Request["password"== null
    ))
    23            
    {
    24                objContext.Response.Write("用户名和密码不允许为空!"
    );
    25
                    objContext.Response.End();
    26            }

    27
    28            string sUserId = ""
    ;
    29            sUserId = objContext.Request["userid"
    ].ToString();
    30            string sPassword = ""
    ;
    31            sPassword = objContext.Request["password"
    ].ToString();
    32            string
    [] strRoles;
    33            strRoles =
     AuthenticateAndGetRoles(sUserId,sPassword);
    34            if ((strRoles == null|| (strRoles.GetLength(0== 0
    ))
    35            
    {
    36                objContext.Response.Write("用户名和密码错误!"
    );
    37                objApp.CompleteRequest();//终止一个http请求

    38            }

    39            //GenericIdentity类表示具有指定名称和身份验证类型的用户
    40            GenericIdentity objIdentity = new GenericIdentity(sUserId, "CustomAuthentication");
    41            objContext.User = new
     GenericPrincipal(objIdentity,strRoles);
    42        }

    43
    44        
    /// <summary>
    45        /// 根据http请求的userid和password来验证和获取角色。
    46        /// </summary>

    47        private string[] AuthenticateAndGetRoles(string sUserId, string sPassword)
    48        
    {
    49            string[] strRoles = null
    ;
    50            if ((sUserId.Equals("xieex")) && (sPassword.Equals("111"
    )))
    51            
    {
    52                strRoles = new String[1
    ];
    53                strRoles[0= "Administrator"
    ;
    54            }

    55            else if ((sUserId.Equals("zhangsan")) && (sPassword.Equals("222")))
    56            
    {
    57                strRoles = new String[1
    ];
    58                strRoles[0= "User"
    ;
    59            }

    60            return strRoles;
    61        }

    62
    63        public void
     Dispose()
    64        
    {
    65
     
    66        }

    67    }

    68}

     

    5、新建一个项目,在Web.config文件中注册该类,当访问该项目的页面时,都要校验userid和password才能访问,并且在该项目的引用中添加MyModule1.dll

    在Web.config文件中添加:

    <!--HttpModule通过对HttpApplication对象的一系列事件的处理来对HTTP处理管道施加影响,这些事件在HttpModule的Init方法中进行注册包括BeginRequest等。-->
          
    <httpModules>
            
    <add name="myModule1" type="MyModule1.SecurityModules,MyModule1"/>
          
    </httpModules>
      
    <!--add将HttpModule类添加到应用程序-->
      
    <!--add格式:<add name="modulename(随便起)" type="命名空间。类名(该类继承IHttpModule接口), (assemblyname)dll文件名"/>  -->

      
    <!--remove从应用程序移除HttpModule类-->
      
    <!--remove格式<remove name="modulename">-->

      
    <!--clear从应用程序移除所有HttpModule映射-->
      
    <!--clear/-->

    此时访问该项目中的页面时,就要校验用户名和密码了。

    此时输入在URL上增加“?userid=xieex@password=111”时就可以进入页面了。

    这样我们就开发完了ISAPI过滤器,注意过滤器可以有多个,只要在web.config中的httpModules节点中Add即可,它们都会起作用的不会覆盖的。

    HttpModule通过对HttpAplication对象的一系列事件的处理来对HTTP处理管道施加影响,这些事件在HttpModule的Init方法中进行注册。具体的事件发生顺序如下:


                             图五  事件发生顺序

    关于事件发生顺序可以参考例子:MultiHttpModule.csproj,这个例子也反应了过滤器可以有多个,只要在web.config中的httpModules节点中Add即可,它们都会起作用的不会覆盖的。

    2、开发“ISAPI扩展”组件

    还是拿下面的例子来说:
    “我自己举例来说吧,我建立了一个.apx文件,要求客户端访问该页面,并在客户端返回"我是apx文件,我是被“ISAPI过滤器”过滤过的,并被"ISAPI扩展"处理过的"。在访问页面时必须需要用户名和密码。
    此时我就需要开发组件来扩充Web服务器,因为Web服务器没有校验指定的用户名和密码的能力也没有处理.apx文件的能力。我就需要开发“ISAPI过滤器”组件(用来校验用户名和密码)和ISAPI扩展组件(处理.apx文件)。”
    上面的ISAPI过滤器可以解决“校验用户名和密码”问题,下面我们利用ISAPI扩展来处理.apx文件。

    HttpHandler实现了ISAPI扩展的功能,它处理请求(Request)的信息和发送响应(Response)。HttpHandler功能的实现要继承IHttpHandler接口。
    HTTP处理程序是实现了System.Web.IHttpHandler接口的.NET组件,任何实现了IHttpHandler接口的类都可以用于处理输入的HTTP请求。

    HttpHandler的实现:
    1、编写一个实现IHttpHandler接口的类;
    2、在Web.config或machine.config文件中注册这个处理程序;
    3、在Internet服务管理器把文件扩展(你想要的文件扩展名)映射到ASP.NET ISAPI扩展DLL(aspnet_isapi.dll)上。(该步也可不用做)

    同样通过一个实例来实现HttpHandler(具体见附件代码)
    1、编写了HandlerAPX类,该类继承了IHttpHandler接口,该类实现了ProcessRequest方法和IsReusable属性。注意要引用System.Web

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Text;
     4using
     System.Web;
     5

     6namespace
     MyHandler
     7
    {
     8    public class
     HandlerAPX : IHttpHandler
     9    
    {
    10        Implementation of IHttpHandler

    35    }

    36}

     

    2、新建一个项目,在Web.config文件中注册该类,并且在该项目的引用中添加MyHandler.dll

    在Web.config文件中添加:

          <httpHandlers>
            
    <add verb="*" path="*.apx" 
                   type
    ="MyHandler.HandlerAPX,MyHandler" />

          
    </httpHandlers>
          
    <!--add格式:<add verb="*" path="要处理文件"  type="命名空间.类名(该类继承IHttpHandler接口), (assemblyname)dll文件名"/>          -->

     

    说明当IIS识别到.apx文件时,就调用HandlerAPX类来处理。

    http请求通过httpHandler来处理,本来系统就对其有默认处理,那和自定义的处理是如何协调的呢?看下图就可以明白,其实两者是选择其一的:


                                         图六  HttpHandler之间的关系

    在“图五 事件发生顺序”中,我们看到HttpHandler建立后,此后Session就可以用了,下面来看看在HttpHandler中如何访问Session.
    1、不能直接通过HttpContext访问
    2、必须实现IRequiresSessionState接口
    3、IRequriresSessionState接口指定目标HTTP处理程序接口具有对会话状态值的读写访问权限,这是一个标记接口,没
       有任何方法。

    同样通过一个实例来实现在HttpHandler访问Session(具体见附件代码)
    1、编写类HandlerSession,该类实现了接口IHttpHandler,IRequiresSessionState

     

    using System;
    using
     System.Collections.Generic;
    using
     System.Text;
    using
     System.Web;
    using
     System.Web.SessionState;

    namespace
     MyHandlerSession
    {
        
    public class
     HandlerSession : IHttpHandler,IRequiresSessionState
        
    {
            
    #region Implementation of IHttpHandler
            
    /// <summary>
            
    /// http处理程序的核心。我们调用这个方法来处理http请求。
            
    /// </summary>

            
    /// <param name="context"></param>

            public void ProcessRequest(HttpContext context)
            
    {
                HttpResponse objResponse 
    =
     context.Response;
                HttpRequest objRequest 
    =
     context.Request;

                HttpSessionState objSession 
    =
     context.Session;
               
                objResponse.Write(
    "<html><body><h1>欢迎使用自定义的HttpHandler!<br>"
    );
                objSession[
    "test"= "Session测试<br>"
    ;
                objResponse.Write(
    "Session的值为:" + objSession["Test"
    ].ToString());
            }


            
    /// <summary>
            
    /// 我们调用这个属性来决定http处理程序的实例是否可以用于处理相同其它类型的请求。
            
    ///
     HTTP处理程序可以返回true或false来表明它们是否可以重复使用。
            
    /// </summary>

            public bool IsReusable
            
    {
                
    get

                
    {
                    
    return true
    ;
                }

            }

            
    #endregion

        }

    }

     

    2、新建一个项目,在Web.config文件中注册该类,并且在该项目的引用中添加MyHandlerSession.dll

    在Web.config文件中添加:

          <httpHandlers>
            
    <add verb="*" path="*.apx"
                   type
    ="MyHandler.HandlerAPX,MyHandler" />

            
    <add verb="*" path="HttpModule.aspx"
           type
    ="MyHandlerSession.HandlerSession,MyHandlerSession" />


          
    </httpHandlers>

     

    说明当IIS识别到HttpModule.aspx文件时,就调用HandlerSession类来处理。但是如果是.apx文件时,就会调用HandlerAPX类来处理。

    也就是说注册多个HttpHandler会覆盖的,这与HttpModule是不一样的。如果是对同一类文件注册了不同的HttpHandler,会执行最后一个。

    即:如果Web.config配置为

          <httpHandlers>
            
    <add verb="*" path="*.apx"
                   type
    ="MyHandler.HandlerAPX,MyHandler" />

            
    <add verb="*" path="*.apx"
           type
    ="MyHandlerSession.HandlerSession,MyHandlerSession" />


          
    </httpHandlers>

    说明当IIS识别到*.apx文件时,就调用HandlerSession类来处理,而不是调用HandlerAPX类来处理。

    三、ASP.NET事件模型机制
    1、ASP.NET之所以对于以前的ASP是一个革命性的巨变,在很大程度上是由于ASP.NET技术是一种基于事件驱动的全新技术。
    2、在ASP.NET中时间的触发和处理是在客户端和服务端进行的。
    3、ASP.NET中,如果频繁和服务器进行事件信息传递,会大大降低服务器的处理效率和性能,因而有些事件如OnMouseOver没有提供;
    4、但提供了Change事件,为了提高效率它们被缓存在客户端,等到再一次事件信息被发送到服务器端时一同发送回去。
    如文本框的change事件,下拉框的change事件,
    如两个控件的change事件中:

     

            protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
            
    {
                Response.Write(
    "DropDownList控件选择改变!<br>"
    );
            }


            
    protected void TextBox1_TextChanged(object sender, EventArgs e)
            
    {
                Response.Write(
    "TextBox文本改变!<br>"
    );
            }

    如果控件本身的AutoPostBack设置为false(默认是false)时,文本框和下拉框发生改变时,不会执行change事件的,而是将事件信息缓存在客户端,当在页面上点击一个服务器端控件Button,

            protected void Button1_Click(object sender, System.EventArgs e)
            
    {
               Response.Write(
    "点击了Button按钮!<br>"
    );
            }

    此时将客户端中的事件信息发送到服务器端,执行所有的事件,返回到客户端的信息为:
    TextBox文本改变!
    DropDownList控件选择改变!
    点击了Button按钮!

    具体例子代码见附件。
    HttpModuelandHttpHandler代码

  • 相关阅读:
    网络模块axios的简单应用
    UWP App国际化的两种实现
    C# 显示函数调用方的详细信息
    UWP SVG 转 Glyph
    UWP 区分设备类型
    Flutter 星标已正式超过React Native
    查看Github星标排行榜
    博客园部分非公开api
    模块化(零):综述
    模块化一:提取模块
  • 原文地址:https://www.cnblogs.com/winner/p/1334085.html
Copyright © 2020-2023  润新知