從專題三開始分析community server的一些具體的技術(shù)實現(xiàn),根據(jù)iis對請求的處理流程,從httpmodule& httphandler切入話題,同時你也可以通過一系列的專題了解cs的運行過程,不只如此,所有的.net 1.1 構(gòu)架的web app都是以同樣的順序執(zhí)行的。
先了解一下iis系統(tǒng)。它是一個程序,負(fù)責(zé)對網(wǎng)站的內(nèi)容進(jìn)行管理并且處理對客戶的請求做出反應(yīng)。當(dāng)用戶對一個頁面提出請求時,iis做如下反應(yīng)(不考慮權(quán)限問題):
1.把對方請求的虛擬路徑轉(zhuǎn)換成物理路徑
2.根據(jù)物理路徑搜索請求的文件
3.找到文件后,獲取文件的內(nèi)容
4.生成http頭信息。
5.向客戶端發(fā)送所有的文件內(nèi)容:首先是頭信息,然后是html內(nèi)容,最后是其它文件的內(nèi)容。
6.客戶端ie瀏覽器獲得信息后,解析文件內(nèi)容,找出其中的引用文件,如.js .css .gif等,向iis請求這些文件。
7.iis獲取請求后,發(fā)送文件內(nèi)容。
8.當(dāng)瀏覽器獲取所有內(nèi)容后,生成內(nèi)容界面,客戶就看到圖像/文本/其它內(nèi)容了。
但是iis本身是不支持動態(tài)頁面的,也就是說它僅僅支持靜態(tài)html頁面的內(nèi)容,對于如.asp,.aspx,.cgi,.php等,iis并不會處理這些標(biāo)記,它就會把它當(dāng)作文本,絲毫不做處理發(fā)送到客戶端。為了解決這個問題。iis有一種機制,叫做isapi的篩選器,這個東西是一個標(biāo)準(zhǔn)組件(com組件),當(dāng)在在訪問iis所不能處理的文件時,如asp.net 1.1 中的iis附加isapi篩選器如圖:

asp.net服務(wù)在注冊到iis的時候,會把每個擴展可以處理的文件擴展名注冊到iis里面(如:*.ascx、*.aspx等)。擴展啟動后,就根據(jù)定義好的方式來處理iis所不能處理的文件,然后把控制權(quán)跳轉(zhuǎn)到專門處理代碼的進(jìn)程中。讓這個進(jìn)程開始處理代碼,生成標(biāo)準(zhǔn)的html代碼,生成后把這些代碼加入到原有的html中,最后把完整的html返回給iis,iis再把內(nèi)容發(fā)送到客戶端。
有上面對isapi的簡單描述,我們把httpmodule& httphandler分開討論,并且結(jié)合cs進(jìn)行具體的實現(xiàn)分析。
httpmodule:
httpmodule實現(xiàn)了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:

要實現(xiàn)httpmodule功能需要如下步驟:
1.編寫一個類,實現(xiàn)ihttpmodule接口
2.實現(xiàn)init 方法,并且注冊需要的方法
3.實現(xiàn)注冊的方法
4.實現(xiàn)dispose方法,如果需要手工為類做一些清除工作,可以添加dispose方法的實現(xiàn),但這不是必需的,通常可以不為dispose方法添加任何代碼。
5.在web.config文件中,注冊您編寫的類
到這里我們還需要了解一個asp.net的運行過程:

在圖中第二步可以看到當(dāng)請求開始的時候,馬上就進(jìn)入了httpmodule,在cs中由于實現(xiàn)了httpmodule的擴展cshttpmodule.cs類,因此當(dāng)一個web請求發(fā)出的時候(如:一個用戶訪問他的blog),cs系統(tǒng)首先調(diào)用cshttpmodule.cs類,并且進(jìn)入
public void init(httpapplication application)
該方法進(jìn)行初始化事件:
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);
有事件就要有對應(yīng)的處理方法:
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)
事件被初始化后就等待系統(tǒng)的觸發(fā),請求進(jìn)入下一步此時系統(tǒng)觸發(fā)application_beginrequest事件,事件處理內(nèi)容如下:
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:為發(fā)出請求的用戶初始化一個context,初始化context用到了線程中本地數(shù)據(jù)槽(localdatastoreslot),把當(dāng)前用戶請求的上下文(contextb)保存在為此請求開辟的內(nèi)存中。
b:判斷是否需要重寫 url(檢查是否需要重寫的過程是對siteurls.config文件中正則表達(dá)式和對應(yīng)url處理的過程),如果需要重寫url,就執(zhí)行asp.net級別上的rewritepath方法獲得新的路徑,新的路徑才是真正的請求信息所在的路徑。這個專題不是講url rewrite,所以只要明白url在這里就進(jìn)行rewrite就可以了,具體的后面專題會敘述。
處理完application_beginrequest 后進(jìn)程繼向下執(zhí)行,隨后觸發(fā)了application_authenticaterequest(如果有朋友不明白這個執(zhí)行過程,可以通過調(diào)試中設(shè)置多個斷點捕獲事件執(zhí)行的順序。如果你還不會調(diào)試,可以留言偷偷的告訴我,嘿嘿。),application_authenticaterequest事件初始化一個context的identity,其實cs提供了很多的identity支持,包括microsoft passport,但是目前的版本中使用的是默認(rèn)值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事件被觸發(fā),事件代碼如下:
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?
//
 注冊會員,創(chuàng)建你的web開發(fā)資料庫,
新聞熱點
疑難解答
圖片精選