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返回给IIS,IIS再把内容发送到客户端。

有上面对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事件初始化一个context的Identity,其实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中分析关键几行代码:

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

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

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

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

处理完上面三个事件后,CS将开始处理请求页面中的具体业务逻辑,如果用户请求的是登录页面,接下来就处理登录页面需要的业务逻辑和呈现,当然这里还会触发一系列其他事件,因为这些事件没有在这里定义我们暂时不做考虑。要说明一点,HttpModule在整个web请求到响应完成过程中都没有退出进程,而是处于监控状态。Application_OnError正是处于其监控范围下的一个事件,一旦有Exception或者继承Exception的类被异常抛出,HttpModule就捕获它,之后就可以根据Exception中ExceptionType值统一处理这些不同的错误信息。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,根据抛出的Exception的ExceptionType类型不同做不同的处理(ForumExceptionType.cs中定义所有的CS ExceptionType)。随后调用Log()保存错误信息到数据库中,以便管理员跟踪这些错误的原因。这里还有重要的一句:CSEvents.CSException(csException)它触发了2个事件类 CSCatastrophicExceptionModule与CSExceptionModule中的处理程序,与 Application_AuthorizeRequest中UserKnown处理机制是一样的,会在以后的专题讨论。只要知道这里会执行 RedirectToMessage方法,把页面重新定向到一个友好的错误显示页即可,如下图所示:

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

原文地址:http://www.cnblogs.com/ugoer/archive/2005/09/06/230917.html

(0)

相关推荐

  • unity实现摄像头跟随

    代码很简单,这里就不多废话了,直接奉上代码 using UnityEngine; using System.Collections; public class FllowTarget : MonoBehaviour { public Transform character; //摄像机要跟随的人物 public float smoothTime = 0.01f; //摄像机平滑移动的时间 private Vector3 cameraVelocity = Vector3.zero; private

  • Community Server专题一:概述Community Server

    Community Server专题一:概述Community Server Community Server(CS)是一个非常优秀的Asp.net开源软件,目前官方发布的系统中包括三个部分:Asp.net Forums.DotText.Gallery.如果你是某个以CS构架网站的会员,你可以很容易的就拥有一个Blog.一个相册.还能在论坛上与他人一起进行讨论,这样就形成一个以User为中心的社区,这也就是起名为 Community Server的意义所在了. CS的构架很巧妙,三套原本不同的开

  • Unity3D游戏引擎实现在Android中打开WebView的实例

    本文讲述了如何在Unity中调用Android中的WebView组件,实现内部浏览器样式的页面切换.首先打开Eclipse创建一个Android的工程: UnityTestActivity.java 入口Activity ,Unity中会调用这个Activity中的方法从而打开网页. package com.xys; import android.content.Context; import android.content.Intent; import android.os.Bundle; i

  • 基于Unity容器中的对象生存期管理分析

    IoC容器的对象生存期管理 如果你一直在使用IoC容器,你可能已经使用过了一些对象生存期管理模型(Object Lifetime Management).通过对对象生存期的管理,将使对象的复用成为可能.同时其使容器可以控制如何创建和管理对象实例. Unity提供的对象生存期管理模型是通过从抽象类LifetimeManager的派生类来完成.Unity将为每个类型的注册创建生存期管理器.每当UnityContainer需要创建一个新的对象实例时,将首先检测该对象类型的生存期管理器,是否已有一个对象

  • Unity3D中脚本的执行顺序和编译顺序

    事件函数的执行顺序 先说一下执行顺序吧. 官方给出的脚本中事件函数的执行顺序如下图: 我们可以做一个小实验来测试一下: 在Hierarchy视图中创建三个游戏对象,在Project视图中创建三条脚本,如下图所示,然后按照顺序将脚本绑定到对应的游戏对象上: 三条脚本的代码完全一样,只是做了一点名称上的区分: using UnityEngine;using System.Collections;public class Scring0 : MonoBehaviour{    void Awake()

  • unity3d发布apk在android虚拟机中运行的详细步骤(unity3d导出android apk)

    unity3d发布apk在android虚拟机中运行的详细步骤(unity3d导出android apk),总的流程分为以下6个步骤: 1.安装java_jdk 2.配置java环境变量 3.更新android的sdk 4.从Unity3d中发布出apk文件 5.创建android虚拟机并运行 6.将apk文件安装到android虚拟机中 (为方便新手,在下面对每个步骤的具体操作及可能遇到的问题详细提一下) 1.安装java_jdk 官网(www.java.com),免费,我安装的文件的名字是j

  • unity实现多点触控代码

    这是我在论坛看到的,unity多点触控.感觉还不错,分享给大家 //用于绑定参照物对象 var target : Transform; //缩放系数 var distance = 10.0; //左右滑动移动速度 var xSpeed = 250.0; var ySpeed = 120.0; //缩放限制系数 var yMinLimit = -20; var yMaxLimit = 80; //摄像头的位置 var x = 0.0; var y = 0.0; //记录上一次手机触摸位置判断用户是

  • unity3d调用手机或电脑摄像头

    功能很实用,代码很简单,这里就不多废话了. WebCamTexture:网络摄像头材质 WebCamTexture.Play() 播放: WebCamTexture.Pause() 暂停: WebCamTexture.Stop() 停止: //经测试此代码可以使用,当你绑定到游戏物体时尽可以了. using unityEngine; using System.Collections; public class Test : MonoBehaviour { public string device

  • Community Server专题三:HttpModule

    从专题三开始分析Community Server的一些具体的技术实现,根据IIS对请求的处理流程,从HttpModule&  HttpHandler切入话题,同时你也可以通过一系列的专题了解CS的运行过程,不只如此,所有的.Net 1.1 构架的Web App都是以同样的顺序执行的. 先了解一下IIS系统.它是一个程序,负责对网站的内容进行管理并且处理对客户的请求做出反应.当用户对一个页面提出请求时,IIS做如下反应(不考虑权限问题): 1.把对方请求的虚拟路径转换成物理路径 2.根据物理路径搜

  • Community Server专题二:体系结构

    Community Server专题二:体系结构 在进行CS细节分析的之前,有必要先了解CS工程(解决方案)的组成,以及组成CS工程中项目的结构,本文分为三个部分:1.工程结构 2.三层构架 3.数据库构架. 1:工程结构 =538) {this.width=538;}" border=0> CS工程主要分为4个部分 a:系统底层构架项目CommunityServerComponents.CommunityServerControls,提供给其他项目父类.接口.全局变量.CS系统设置.公用

  • Python 专题三 字符串的基础知识

    在Python中最重要的数据类型包括字符串.列表.元组和字典等.该篇主要讲述Python的字符串基础知识. 一.字符串基础 字符串指一有序的字符序列集合,用单引号.双引号.三重(单双均可)引号引起来.如: s1='www.csdn.NET'   s2="www.csdn.Net"   s3='''aaabbb''' 其中字符串又包括: 1.转义字符串 像C语言中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,python也有转义字符.如下:   \

  • SQL SERVER 2008数据库引擎详细介绍

    SQL Server 的数据库引擎组件是用于存储.处理数据和保证数据安全的核心服务.数据库引擎提供受控的访问和快速事务处理,以满足企业中要求极高.大量使用数据的应用程序的要求. SQL Server 支持在同一台计算机上最多存在 50 个数据库引擎实例.对于本地安装,必须以管理员身份运行安装程序.如果从远程共享安装 SQL Server,则必须使用对远程共享具有读取和执行权限的域帐户. 高可用性解决方案概述 高可用性解决方案可减少硬件或软件故障造成的影响,保持应用程序的可用性,尽可能地减少用户所

  • ASP.NET2.0数据库入门之SQL Server

    因为Access并不真正为高性能应用程序服务,所以一个希望有多个同时连接用户的站点必须部署一个比Access更适合的数据源.本文将讲述如何从Microsoft SQL Server(一种企业级RDMS)中获取数据. SQL Server完全版包括了三个部分.第一个是引擎,用于实际组织数据以及针对命令响应进行读取和写入操作.第二个是开发人员的工具软件包,用于对数据库进行操作,例如Query Analyzer和Data Transformation Services.最后一个是用于管理数据的工具,包

  • Caddy 一个用Go实现的Web Server

    这是一个Web Server的时代,apache2与nginx共舞,在追求极致性能的路上,没有最高,只有更高.但这又是一个追求个性化的时代,有些Web Server并没有去挤"Performance提升"这一独木桥,而是有着自己的定位,Caddy就是这样一个开源Web Server. Caddy的作者Matt Holt在caddy官网以及FAQ中对caddy的目标阐释如下: 其他Web Server为Web而设计,Caddy为human设计.功能定位上,与经常充当最前端反向代理的ngi

  • Linux系统通过Docker安装SQL Server数据库

    目录 一.前言 二.安装SQL Server 1.拉取SQL Server镜像 2.创建目录 3.运行容器 4.使用命令进入SQL Server 5.使用SSMS登录SQL Server数据库 三.总结 一.前言 现在.NET Core已经跨平台了,支持Windows.Linux.Mac系统,而我们也已经在Linux上面使用了Docker.使用.NET开发的人使用最多的就是SQL Server数据,以前是只能在Windows系统上使用,但是从SQL Server 2017开始支持运行在docke

  • Windows系统彻底卸载SQL Server通用方法(推荐!)

    目录 前言 一.停止 SQL Server 服务 二.卸载 SQL Server 数据库 三.删除 SQL Server 相关文件 四.删除 SQL Server 相关注册表 五.重启电脑 总结 前言 无论什么时候,SQL Server 的安装和卸载都是一件让我们头疼的事情.因为不管是 SQL Server 还是 MySQL 的数据库,当我们在使用数据库时因为未知原因出现问题,想要卸载重装时,如果数据库卸载不干净,就会导致数据库重装失败.所以学会卸载 SQL Server 还是很重要的,本篇文章

  • python模拟Django框架实例

    一.python实现web服务器 web开发首先要有web服务器才行.比如apache,但是在开发阶段最好有一个简单方便的开发服务器, 容易重启进行调试,等开发调试完毕后,再将代码部署到成熟稳定高效的web服务器. # -*- coding: utf-8 -*- from wsgiref import simple_server # 定义一个输出 hello world 和环境变量的简单web应用程序 def hello_app(environ, start_response): # 输出 ht

随机推荐