欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Community Server专题三:HttpModule

程序员文章站 2022-07-19 17:32:43
从专题三开始分析community server的一些具体的技术实现,根据iis对请求的处理流程,从httpmodule&  httphandler切入...
从专题三开始分析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 = "
            const string nowww = "
            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 = "
            const string nowww = "
            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也就不在话下了。希望我的这些分析能对你有帮助。


原文地址: