• Community Server专题三:HttpModule


    从专题三开始分析Community Server的一些具体的技术实现,根据IIS对请求的处理流程,从HttpModule& HttpHandler切入话题,同时你也可以通过一系列的专题了解CS的运行过程,不只如此,所有的.Net 1.1 构架的Web App都是以同样的顺序执行的。

    先了解一下IIS系统。它是一个程序,负责对网站的内容进行管理并且处理对客户的请求做出反应。当用户对一个页面提出请求时,IIS做如下反应(不考虑权限问题):

    1.把对方请求的虚拟路径转换成物理路径

    2.根据物理路径搜索请求的文件

    3.找到文件后,获取文件的内容

    4.生成Http头信息。

    5.向客户端发送所有的文件内容:首先是头信息,然后是Html内容,最后是其它文件的内容

    6.客户端IE浏览器获得信息后,解析文件内容,找出其中的引用文件,如.js .css .gif等,向IIS请求这些文件。

    7.IIS获取请求后,发送文件内容。

    8.当浏览器获取所有内容后,生成内容界面,客户就看到图像/文本/其它内容了。

    但是IIS本身是不支持动态页面的,也就是说它仅仅支持静态html页面的内容,对于如.asp.aspx.cgi.php等,IIS并不会处理这些标记,它就会把它当作文本,丝毫不做处理发送到客户端。为了解决这个问题。IIS有一种机制,叫做ISAPI的筛选器,这个东西是一个标准组件(COM组件),当在在访问IIS所不能处理的文件时,如asp.net 1.1 中的IIS附加ISAPI筛选器如图:

    Asp.net服务在注册到IIS的时候,会把每个扩展可以处理的文件扩展名注册到IIS里面(如:*.ascx*.aspx)。扩展启动后,就根据定义好的方式来处理IIS所不能处理的文件,然后把控制权跳转到专门处理代码的进程中。让这个进程开始处理代码,生成标准的HTML代码,生成后把这些代码加入到原有的Html中,最后把完整的Html返回给IISIIS再把内容发送到客户端。

        有上面对ISAPI的简单描述,我们把HttpModule& HttpHandler分开讨论,并且结合CS进行具体的实现分析。

    HttpModule

    HttpModule实现了ISAPI Filter的功能,是通过对IhttpModule接口的继承来处理。下面打开CS中的CommunityServerComponents项目下的CSHttpModule.cs文件(放在HttpModule目录)

    //------------------------------------------------------------------------------
    // <copyright company="Telligent Systems">
    //     Copyright (c) Telligent Systems Corporation.  All rights reserved.
    // </copyright> 
    //------------------------------------------------------------------------------

    using System;
    using System.IO;
    using System.Web;
    using CommunityServer.Components;
    using CommunityServer.Configuration;

    namespace CommunityServer 
    {

        
    // *********************************************************************
        
    //  CSHttpModule
        
    //
        /**//// <summary>
        
    /// This HttpModule encapsulates all the forums related events that occur 
        
    /// during ASP.NET application start-up, errors, and end request.
        
    /// </summary>

        // ***********************************************************************/
        public class CSHttpModule : IHttpModule 
        
    {
            
    Member variables and inherited properties / methods#region Member variables and inherited properties / methods

            
    public String ModuleName 
            

                
    get return "CSHttpModule"; } 
            }
        


            
    // *********************************************************************
            
    //  ForumsHttpModule
            
    //
            /**//// <summary>
            
    /// Initializes the HttpModule and performs the wireup of all application
            
    /// events.
            
    /// </summary>
            
    /// <param name="application">Application the module is being run for</param>

            public void Init(HttpApplication application) 
            

                
    // Wire-up application events
                
    //
                application.BeginRequest += new EventHandler(this.Application_BeginRequest);
                application.AuthenticateRequest 
    += new EventHandler(Application_AuthenticateRequest);
                application.Error 
    += new EventHandler(this.Application_OnError);
                application.AuthorizeRequest 
    += new EventHandler(this.Application_AuthorizeRequest);
                
                
    //settingsID = SiteSettingsManager.GetSiteSettings(application.Context).SettingsID;
                Jobs.Instance().Start();
                
    //CSException ex = new CSException(CSExceptionType.ApplicationStart, "Appication Started " +  AppDomain.CurrentDomain.FriendlyName);
                
    //ex.Log();
            }


            
    //int settingsID;
            public void Dispose() 
            
    {
                
    //CSException ex = new CSException(CSExceptionType.ApplicationStop, "Application Stopping " +  AppDomain.CurrentDomain.FriendlyName);
                
    //ex.Log(settingsID);
                Jobs.Instance().Stop();
            }


            
    Installer#region Installer




            
    #endregion



            
    #endregion


            
    Application OnError#region Application OnError
            
    private void Application_OnError (Object source, EventArgs e) 
            
    {
                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;
                
                CSException csException 
    = context.Server.GetLastError() as CSException;

                
    if(csException == null)
                    csException 
    = context.Server.GetLastError().GetBaseException() as CSException;

                
    try
                
    {
                    
    if (csException != null)
                    
    {
                        
    switch (csException.ExceptionType) 
                        
    {
                            
    case CSExceptionType.UserInvalidCredentials:
                            
    case CSExceptionType.AccessDenied:
                            
    case CSExceptionType.AdministrationAccessDenied:
                            
    case CSExceptionType.ModerateAccessDenied:
                            
    case CSExceptionType.PostDeleteAccessDenied:
                            
    case CSExceptionType.PostProblem:
                            
    case CSExceptionType.UserAccountBanned:
                            
    case CSExceptionType.ResourceNotFound:
                            
    case CSExceptionType.UserUnknownLoginError:
                            
    case CSExceptionType.SectionNotFound:
                                csException.Log();
                                
    break;
                        }

                    }
     
                    
    else 
                    
    {
                        Exception ex 
    = context.Server.GetLastError();
                        
    if(ex.InnerException != null)
                            ex 
    = ex.InnerException;

                        csException 
    = new CSException(CSExceptionType.UnknownError, ex.Message, context.Server.GetLastError());

                        System.Data.SqlClient.SqlException sqlEx 
    = ex as System.Data.SqlClient.SqlException;
                        
    if(sqlEx == null || sqlEx.Number != -2//don't log time outs
                            csException.Log();
                    }

                }

                
    catch{} //not much to do here, but we want to prevent infinite looping with our error handles

                CSEvents.CSException(csException);
            }



            
    #endregion



            
    Application AuthenticateRequest#region Application AuthenticateRequest

            
    private void Application_AuthenticateRequest(Object source, EventArgs e) 
            
    {
                HttpContext context 
    = HttpContext.Current;
                Provider p 
    = null;
                ExtensionModule module 
    = null;

                
    // If the installer is making the request terminate early
                if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer) {
                    
    return;
                }

                
                
    // Only continue if we have a valid context
                
    //
                if ((context == null|| (context.User == null))
                    
    return;

                
    try 
                
    {
                    
    // Logic to handle various authentication types
                    
    //
                    switch(context.User.Identity.GetType().Name.ToLower())
                    
    {

                            
    // Microsoft passport
                        case "passportidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Windows
                        case "windowsidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Forms
                        case "formsidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Custom
                        case "customidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                        
    default:
                            CSContext.Current.UserName 
    = context.User.Identity.Name;
                            
    break;

                    }


                }
     
                
    catch( Exception ex ) 
                
    {
                    CSException forumEx 
    = new CSException( CSExceptionType.UnknownError, "Error in AuthenticateRequest", ex );
                    forumEx.Log();

                    
    throw forumEx;
                }


                
    //            // Get the roles the user belongs to
                
    //            //
                
    //            Roles roles = new Roles();
                
    //            roles.GetUserRoles();
            }

            
    #endregion


            
    Application AuthorizeRequest#region Application AuthorizeRequest
            
    private void Application_AuthorizeRequest (Object source, EventArgs e) {


                
    if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer)
                
    {
                    
    //CSContext.Create(context);
                    return;
                }



                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;

                CSContext csContext 
    = CSContext.Current;
                
    //bool enableBannedUsersToLogin = CSContext.Current.SiteSettings.EnableBannedUsersToLogin;
                
    //            // If the installer is making the request terminate early
    //            if (csContext.ApplicationType == ApplicationType.Installer) {
    //                return;
    //            }

                
    //csContext.User = CSContext.Current.User;

                CSEvents.UserKnown(csContext.User);

                ValidateApplicationStatus(csContext);

                
    // Track anonymous users
                
    //
                Users.TrackAnonymousUsers(context);

                
    // Do we need to force the user to login?
                
    //
                
                
    if (context.Request.IsAuthenticated) 
                
    {
                    
    string username = context.User.Identity.Name;
                    
    if (username != null
                    
    {
                        
    string[] roles = CommunityServer.Components.Roles.GetUserRoleNames(username);
                        
    if (roles != null && roles.Length > 0
                        
    {
                            csContext.RolesCacheKey 
    = string.Join(",",roles);
                        }

                    }

                }

            }


            
    #endregion


            
    Application BeginRequest#region Application BeginRequest
            
    private void Application_BeginRequest(Object source, EventArgs e) 
            
    {
                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;

                
                CSConfiguration config 
    = CSConfiguration.GetConfig();
                
                
    // If the installer is making the request terminate early
                if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
                
    {
                    
    //CSContext.Create(context);
                    return;
                }


                CheckWWWStatus(config,context);

                

                CSContext.Create(context, ReWriteUrl(context));

                                        
            }


            
    private void CheckWWWStatus(CSConfiguration config, HttpContext context)
            
    {
                
    if(config.WWWStatus == WWWStatus.Ignore)
                    
    return;

                
    const string withWWW = "http://www.";
                
    const string noWWW = "http://";
                
    string rawUrl = context.Request.Url.ToString().ToLower();
                
    bool isWWW = rawUrl.StartsWith(withWWW);

                
                
    if(config.WWWStatus == WWWStatus.Remove && isWWW)
                
    {
                    context.Response.Redirect(rawUrl.Replace(withWWW, noWWW));
                }

                
    else if(config.WWWStatus == WWWStatus.Require && !isWWW)
                
    {
                    context.Response.Redirect(rawUrl.Replace(noWWW, withWWW));
                }

                
            
            }


            
    ReWriteUrl#region ReWriteUrl
            
    private bool ReWriteUrl(HttpContext context)
            
    {

                
    // we're now allowing each individual application to be turned on and off individually. So before we allow
                
    // a request to go through we need to check if this product is disabled and the path is for the disabled product,
                
    // if so we display the disabled product page.
                
    //
                
    // I'm also allowing the page request to go through if the page request is for an admin page. In the past if you 
                
    // disabled the forums you were locked out, now with this check, even if you're not on the same machine but you're accessing
                
    // an admin path the request will be allowed to proceed, where the rest of the checks will ensure that the user has the
                
    // permission to access the specific url.

                
    // Url Rewriting
                
    //
                
    //RewriteUrl(context);

                
    string newPath = null;
                
    string path = context.Request.Path;
                
    bool isReWritten = SiteUrls.RewriteUrl(path,context.Request.Url.Query,out newPath);

                
    //very wachky. The first call into ReWritePath always fails with a 404.
                
    //calling ReWritePath twice actually fixes the probelm as well. Instead, 
                
    //we use the second ReWritePath overload and it seems to work 100% 
                
    //of the time.
                if(isReWritten && newPath != null)
                
    {
                    
    string qs = null;
                    
    int index = newPath.IndexOf('?');
                    
    if (index >= 0)
                    
    {
                        qs 
    = (index < (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
                        newPath 
    = newPath.Substring(0, index);
                    }

                    context.RewritePath(newPath,
    null,qs);
                }


                
    return isReWritten;
            }


            
    #endregion


            
    private void ValidateApplicationStatus(CSContext cntx)
            
    {
                
    if(!cntx.User.IsAdministrator)
                
    {
                    
    string disablePath = null;
                    
    switch(cntx.Config.AppLocation.CurrentApplicationType)
                    
    {
                        
    case ApplicationType.Forum:
                            
    if(cntx.SiteSettings.ForumsDisabled)
                                disablePath 
    = "ForumsDisabled.htm";
                            
    break;
                        
    case ApplicationType.Weblog:
                            
    if(cntx.SiteSettings.BlogsDisabled)
                                disablePath 
    = "BlogsDisabled.htm";
                            
    break;
                        
    case ApplicationType.Gallery:
                            
    if(cntx.SiteSettings.GalleriesDisabled)
                                disablePath 
    = "GalleriesDisabled.htm";
                            
    break;
                        
    case ApplicationType.GuestBook:
                            
    if(cntx.SiteSettings.GuestBookDisabled)
                                disablePath 
    = "GuestBookDisabled.htm";
                            
    break;
                        
    case ApplicationType.Document:                   //新增 ugoer
                            if(cntx.SiteSettings.DocumentDisabled)
                                disablePath 
    = "DocumentsDisabled.htm";
                            
    break;
                    }


                    
    if(disablePath != null)
                    
    {

                        
    string errorpath = cntx.Context.Server.MapPath(string.Format("~/Languages/{0}/errors/{1}",cntx.Config.DefaultLanguage,disablePath));
                        
    using(StreamReader reader = new StreamReader(errorpath))
                        
    {
                            
    string html = reader.ReadToEnd();
                            reader.Close();

                            cntx.Context.Response.Write(html);
                            cntx.Context.Response.End();
                        }

                    }

                }

            }


            
    #endregion



        }


    }

    Web.Config中的配置:

            <httpModules>
                
    <add name="CommunityServer" type="CommunityServer.CSHttpModule, CommunityServer.Components" />
                
    <add name="Profile" type="Microsoft.ScalableHosting.Profile.ProfileModule, MemberRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7c773fb104e7562"/>
                
    <add name="RoleManager" type="Microsoft.ScalableHosting.Security.RoleManagerModule, MemberRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7c773fb104e7562" />
            
    </httpModules>

    CSHttpModule.cs   UML

    要实现HttpModule功能需要如下步骤:

    1.编写一个类,实现IhttpModule接口

    2.实现Init 方法,并且注册需要的方法

    3.实现注册的方法

    4.实现Dispose方法,如果需要手工为类做一些清除工作,可以添加Dispose方法的实现,但这不是必需的,通常可以不为Dispose方法添加任何代码。

    5.Web.config文件中,注册您编写的类

    到这里我们还需要了解一个Asp.Net的运行过程:

    在图中第二步可以看到当请求开始的时候,马上就进入了HttpModule,在CS中由于实现了HttpModule的扩展CSHttpModule.cs类,因此当一个web请求发出的时候(如:一个用户访问他的blog),CS系统首先调用CSHttpModule.cs类,并且进入

    public void Init(HttpApplication application)

    该方法进行初始化事件:

    application.BeginRequest += new EventHandler(this.Application_BeginRequest);

    application.AuthenticateRequest += new EventHandler(Application_AuthenticateRequest);

    application.Error += new EventHandler(this.Application_OnError);

    application.AuthorizeRequest += new EventHandler(this.Application_AuthorizeRequest);

    有事件就要有对应的处理方法:

    private void Application_BeginRequest(Object source, EventArgs e)

    private void Application_AuthenticateRequest(Object source, EventArgs e)

    private void Application_OnError (Object source, EventArgs e)

    private void Application_AuthorizeRequest (Object source, EventArgs e)

    事件被初始化后就等待系统的触发,请求进入下一步此时系统触发Application_BeginRequest事件,事件处理内容如下:

    private void Application_BeginRequest(Object source, EventArgs e) 
            
    {
                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;

                
                CSConfiguration config 
    = CSConfiguration.GetConfig();
                
                
    // If the installer is making the request terminate early
                if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
                
    {
                    
    //CSContext.Create(context);
                    return;
                }


                CheckWWWStatus(config,context);

                

                CSContext.Create(context, ReWriteUrl(context));

                                        
            }


            
    private void CheckWWWStatus(CSConfiguration config, HttpContext context)
            
    {
                
    if(config.WWWStatus == WWWStatus.Ignore)
                    
    return;

                
    const string withWWW = "http://www.";
                
    const string noWWW = "http://";
                
    string rawUrl = context.Request.Url.ToString().ToLower();
                
    bool isWWW = rawUrl.StartsWith(withWWW);

                
                
    if(config.WWWStatus == WWWStatus.Remove && isWWW)
                
    {
                    context.Response.Redirect(rawUrl.Replace(withWWW, noWWW));
                }

                
    else if(config.WWWStatus == WWWStatus.Require && !isWWW)
                
    {
                    context.Response.Redirect(rawUrl.Replace(noWWW, withWWW));
                }

                
            
            }


            
    ReWriteUrl#region ReWriteUrl
            
    private bool ReWriteUrl(HttpContext context)
            
    {

                
    // we're now allowing each individual application to be turned on and off individually. So before we allow
                
    // a request to go through we need to check if this product is disabled and the path is for the disabled product,
                
    // if so we display the disabled product page.
                
    //
                
    // I'm also allowing the page request to go through if the page request is for an admin page. In the past if you 
                
    // disabled the forums you were locked out, now with this check, even if you're not on the same machine but you're accessing
                
    // an admin path the request will be allowed to proceed, where the rest of the checks will ensure that the user has the
                
    // permission to access the specific url.

                
    // Url Rewriting
                
    //
                
    //RewriteUrl(context);

                
    string newPath = null;
                
    string path = context.Request.Path;
                
    bool isReWritten = SiteUrls.RewriteUrl(path,context.Request.Url.Query,out newPath);

                
    //very wachky. The first call into ReWritePath always fails with a 404.
                
    //calling ReWritePath twice actually fixes the probelm as well. Instead, 
                
    //we use the second ReWritePath overload and it seems to work 100% 
                
    //of the time.
                if(isReWritten && newPath != null)
                
    {
                    
    string qs = null;
                    
    int index = newPath.IndexOf('?');
                    
    if (index >= 0)
                    
    {
                        qs 
    = (index < (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
                        newPath 
    = newPath.Substring(0, index);
                    }

                    context.RewritePath(newPath,
    null,qs);
                }


                
    return isReWritten;
            }


            
    #endregion

    这个事件主要做两个事情

    a:为发出请求的用户初始化一个Context,初始化Context用到了线程中本地数据槽(LocalDataStoreSlot),把当前用户请求的上下文(contextb)保存在为此请求开辟的内存中。

    b:判断是否需要重写 URL(检查是否需要重写的过程是对SiteUrls.config文件中正则表达式和对应Url处理的过程),如果需要重写URL,就执行asp.net级别上的RewritePath方法获得新的路径,新的路径才是真正的请求信息所在的路径。这个专题不是讲URL Rewrite,所以只要明白URL在这里就进行Rewrite就可以了,具体的后面专题会叙述。

    处理完Application_BeginRequest 后进程继向下执行,随后触发了Application_AuthenticateRequest(如果有朋友不明白这个执行过程,可以通过调试中设置多个断点捕获事件执行的顺序。如果你还不会调试,可以留言偷偷的告诉我,嘿嘿。),Application_AuthenticateRequest事件初始化一个contextIdentity,其实CS提供了很多的Identity支持,包括Microsoft passport,但是目前的版本中使用的是默认值System.Web.Security.FormsIdentity。具体代码如下:

    private void Application_AuthenticateRequest(Object source, EventArgs e) 
            
    {
                HttpContext context 
    = HttpContext.Current;
                Provider p 
    = null;
                ExtensionModule module 
    = null;

                
    // If the installer is making the request terminate early
                if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer) {
                    
    return;
                }

                
                
    // Only continue if we have a valid context
                
    //
                if ((context == null|| (context.User == null))
                    
    return;

                
    try 
                
    {
                    
    // Logic to handle various authentication types
                    
    //
                    switch(context.User.Identity.GetType().Name.ToLower())
                    
    {

                            
    // Microsoft passport
                        case "passportidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Windows
                        case "windowsidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Forms
                        case "formsidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                            
    // Custom
                        case "customidentity":
                            p 
    = (Provider) CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
                            module 
    = ExtensionModule.Instance(p);
                            
    if(module != null)
                                module.ProcessRequest();
                            
    else
                                
    goto default;
                            
    break;

                        
    default:
                            CSContext.Current.UserName 
    = context.User.Identity.Name;
                            
    break;

                    }


                }
     
                
    catch( Exception ex ) 
                
    {
                    CSException forumEx 
    = new CSException( CSExceptionType.UnknownError, "Error in AuthenticateRequest", ex );
                    forumEx.Log();

                    
    throw forumEx;
                }


                
    //            // Get the roles the user belongs to
                
    //            //
                
    //            Roles roles = new Roles();
                
    //            roles.GetUserRoles();
            }


    再下来是Application_AuthorizeRequest事件被触发,事件代码如下:

    private void Application_AuthorizeRequest (Object source, EventArgs e) {


                
    if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer)
                
    {
                    
    //CSContext.Create(context);
                    return;
                }



                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;

                CSContext csContext 
    = CSContext.Current;
                
    //bool enableBannedUsersToLogin = CSContext.Current.SiteSettings.EnableBannedUsersToLogin;
                
    //            // If the installer is making the request terminate early
    //            if (csContext.ApplicationType == ApplicationType.Installer) {
    //                return;
    //            }

                
    //csContext.User = CSContext.Current.User;

                CSEvents.UserKnown(csContext.User);

                ValidateApplicationStatus(csContext);

                
    // Track anonymous users
                
    //
                Users.TrackAnonymousUsers(context);

                
    // Do we need to force the user to login?
                
    //
                
                
    if (context.Request.IsAuthenticated) 
                
    {
                    
    string username = context.User.Identity.Name;
                    
    if (username != null
                    
    {
                        
    string[] roles = CommunityServer.Components.Roles.GetUserRoleNames(username);
                        
    if (roles != null && roles.Length > 0
                        
    {
                            csContext.RolesCacheKey 
    = string.Join(",",roles);
                        }

                    }

                }

            }

    Application_AuthorizeRequest中分析关键几行代码:

    1CSContext csContext = CSContext.Current;  //该代码取出在前一个事件中保存在LocalDataStoreSlot中的Context,说明白点就是从内存中取出之前保存的一些数据。

    2CSEvents.UserKnown(csContext.User); //这里触发了一个UserKnown事件,涉及到CS中大量使用委托与事件的一个类CSApplicationCSApplication.cs文件),后续对这个类做专题分析,这里只要先了解该事件起到判断登陆用户是否ForceLogin以及登录的帐户是否是禁用就可以了(把对user的判断移入Application_AuthorizeRequest事件处理程序中是很好的一种处理方法

    3ValidateApplicationStatus(csContext); //判断论坛、blog、相册是否被禁用,如果登录用户的角色不为IsAdministrator,就跳转到相应的禁用警告页面,如Blog被禁用即跳转到BlogsDisabled.htm页面显示。

    4Users.TrackAnonymousUsers(context); //如果是匿名用户,在这个方法中跟踪记录。

    处理完上面三个事件后,CS将开始处理请求页面中的具体业务逻辑,如果用户请求的是登录页面,接下来就处理登录页面需要的业务逻辑和呈现,当然这里还会触发一系列其他事件,因为这些事件没有在这里定义我们暂时不做考虑。要说明一点,HttpModule在整个web请求到响应完成过程中都没有退出进程,而是处于监控状态。Application_OnError正是处于其监控范围下的一个事件,一旦有Exception或者继承Exception的类被异常抛出,HttpModule就捕获它,之后就可以根据ExceptionExceptionType值统一处理这些不同的错误信息。CS中就是这样实现错误处理的,具体的我们看一下代码:

    private void Application_OnError (Object source, EventArgs e) 
            
    {
                HttpApplication application 
    = (HttpApplication)source;
                HttpContext context 
    = application.Context;
                
                CSException csException 
    = context.Server.GetLastError() as CSException;

                
    if(csException == null)
                    csException 
    = context.Server.GetLastError().GetBaseException() as CSException;

                
    try
                
    {
                    
    if (csException != null)
                    
    {
                        
    switch (csException.ExceptionType) 
                        
    {
                            
    case CSExceptionType.UserInvalidCredentials:
                            
    case CSExceptionType.AccessDenied:
                            
    case CSExceptionType.AdministrationAccessDenied:
                            
    case CSExceptionType.ModerateAccessDenied:
                            
    case CSExceptionType.PostDeleteAccessDenied:
                            
    case CSExceptionType.PostProblem:
                            
    case CSExceptionType.UserAccountBanned:
                            
    case CSExceptionType.ResourceNotFound:
                            
    case CSExceptionType.UserUnknownLoginError:
                            
    case CSExceptionType.SectionNotFound:
                                csException.Log();
                                
    break;
                        }

                    }
     
                    
    else 
                    
    {
                        Exception ex 
    = context.Server.GetLastError();
                        
    if(ex.InnerException != null)
                            ex 
    = ex.InnerException;

                        csException 
    = new CSException(CSExceptionType.UnknownError, ex.Message, context.Server.GetLastError());

                        System.Data.SqlClient.SqlException sqlEx 
    = ex as System.Data.SqlClient.SqlException;
                        
    if(sqlEx == null || sqlEx.Number != -2//don't log time outs
                            csException.Log();
                    }

                }

                
    catch{} //not much to do here, but we want to prevent infinite looping with our error handles

                CSEvents.CSException(csException);
            }

    当抛出Exception后,CS开始处理Application_OnError,根据抛出的ExceptionExceptionType类型不同做不同的处理(ForumExceptionType.cs中定义所有的CS ExceptionType)。随后调用Log()保存错误信息到数据库中,以便管理员跟踪这些错误的原因。这里还有重要的一句:CSEvents.CSException(csException)它触发了2个事件类CSCatastrophicExceptionModuleCSExceptionModule中的处理程序,与Application_AuthorizeRequestUserKnown处理机制是一样的,会在以后的专题讨论。只要知道这里会执行RedirectToMessage方法,把页面重新定向到一个友好的错误显示页即可,如下图所示:

     

    至此,CSHttpModule类已经全部分析完毕。在CS里还有另外两个HttpModule,属于Membership范畴,由于CS引用的是Membership的程序集无非进行内部的运行细节分析,但是工作原理与CSHttpModule是一致的,当你真正理解CSHttpModule的时候要去分析其他HttpModule也就不在话下了。希望我的这些分析能对你有帮助。

  • 相关阅读:
    【水】希望之花
    如何不用狄利克雷卷积证明莫比乌斯函数性质二
    【数学】gcd
    挂分宝典
    [luogu P6042]「ACOI2020」学园祭 题解
    [luogu P6041]「ACOI2020」布丁暗杀计划 题解
    11.19模拟
    「CSP-S2020」题解
    11.11模拟
    「洛谷P1445」樱花
  • 原文地址:https://www.cnblogs.com/wzyexf/p/364283.html
Copyright © 2020-2023  润新知