首页 > 开发 > 综合 > 正文

Community Server专题三:HttpModule

2024-07-21 02:29:28
字体:
来源:转载
供稿:网友

从专题三开始分析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?
            //
       注册会员,创建你的web开发资料库,

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表