ASP.NET架構及開發技術詳解教程

才智咖 人氣:2.64W

首先我們來說一下Asp.net工作原理。

ASP.NET架構及開發技術詳解教程

具體描述下這樣的:首先客戶請求WEB頁。然後WEB服務尋找指令檔案(.aspx),而這時就是aspnet_isapi.dll這個動態連線庫來處理。接著Asp.net程式碼被髮送到公共語言執行時進行編譯。接著HTML流返回給瀏覽器和令。最後由瀏覽器處理HTML並顯示頁面。

什麼是ISAPI呢?

在Internet時代的開端,客戶端的需求非常有限;.htm檔案就可以滿足他們的需求。但是,隨著時間的流逝,客戶端需求的擴充超越了.htm檔案或靜態檔案所包含的功能。

開發者需要擴充或擴充套件Web伺服器的功能。Web伺服器廠商設計了不同的解決方案,但是都遵循同一個主題“向Web伺服器插入某些元件”。所有的Web伺服器補充技術都允許開發者建立並插入元件以增強Web伺服器的功能。微軟公司提出了ISAPI(Internet伺服器API),網景公司提出了 NSAPI(網景伺服器API)等等。ISAPI是一種重要的技術,它允許我們增強與ISAPI相容的Web伺服器(IIS就是一種與ISAPI相容的 Web伺服器)的能力。我們使用下面的元件達到這個目的:

1,ISAPI擴充套件:ISAPI擴充套件是使用Win32動態連結庫來實現的。你可以把ISAPI擴充套件看作是一個普通的應用程式。ISAPI擴充套件的處理目標是http請求。

2,ISAPI過濾器:客戶端每次向伺服器發出請求的時候,請求要經過過濾器。客戶端不需要在請求中指定過濾器,只需要簡單地把請求傳送給Web伺服器,接著Web伺服器把請求傳遞給相關的過濾器。接下來過濾器可能修改請求,執行某些登入操作等等。

ASP.NET請求的處理過程:

ASP.NET請求處理過程是基於管道模型的,在模型中ASP.NET把http請求傳遞給管道中的所有模組。每個模組都接收http請求並有完全控制權限。模組可以用任何自認為適合的方式來處理請求。一旦請求經過了所有HTTP模組,就最終被HTTP處理程式處理。HTTP處理程式對請求進行一些處理,並且結果將再次經過管道中HTTP模組。

ISAPI的篩選器:

IIS本身是不支援動態頁面的,也就是說它僅僅支援靜態html頁面的內容,對於如.asp,.aspx,.cgi,.php等,IIS並不會處理這些標記,它就會把它當作文字,絲毫不做處理髮送到客戶端。為了解決這個問題。IIS有一種機制,叫做ISAPI的篩選器,它是一個標準組件(COM元件)。

Asp.net服務在註冊到IIS的時候,會把每個擴充套件可以處理的副檔名註冊到IIS裡面(如:*.ascx、*.aspx等)。擴充套件啟動後,就根據定義好的方式來處理IIS所不能處理的檔案,然後把控制權跳轉到專門處理程式碼的程序中讓這個程序開始處理程式碼,生成標準的.HTML程式碼,生成後把這些程式碼加入到原有的Html中,最後把完整的Html返回給IIS,IIS再把內容傳送到客戶端。

HttpModule:

HttpModule實現了ISAPI Filter的功能,是通過對IhttpModule介面的繼承來處理。

HTTP模組是實現了System.Web.IhttpModule介面的.NET元件。這些元件通過在某些事件中註冊自身,把自己插入ASP.NET請求處理管道。當這些事件發生的時候,ASP.NET呼叫對請求有興趣的HTTP模組,這樣該模組就能處理請求了。

HttpModule的實現:

1. 編寫一個類,實現IhttpModule介面。

2. 實現Init 方法,並且註冊需要的方法。

3. 實現註冊的方法。

4. 實現Dispose方法,如果需要手工為類做一些清除工作,可以新增Dispose方法的實現,但這不是必需的,通常可以不為Dispose方法新增任何程式碼。

5. 在Web.config檔案中,註冊您編寫的類。

下邊我們來看例子,HttpModule的實現:

首先新增一個類庫,然後在引用裡引用System.Web和System.Security這兩個名稱空間。然後寫個類,程式碼如下:

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

using System.Security.Principal;

namespace Httplibrary

{

public class SecurityModules:IHttpModule

{

public void Init(HttpApplication r_objApplication)

{

// 向Application 物件註冊事件處理程式,核心部分。

r_objApplication.AuthenticateRequest += new EventHandler(this.AuthenticateRequest) ;

}

public void Dispose()

{

}

private void AuthenticateRequest(object r_objSender,EventArgs r_objEventArgs)

{

// 鑑別使用者的憑證,並找出使用者角色。

HttpApplication objApp = (HttpApplication) r_objSender ;

HttpContext objContext = (HttpContext) objApp.Context ;

if ( (objApp.Request["userid"] == null) ||(objApp.Request["password"] == null) )

{

objContext.Response.Write("使用者名稱和密碼為空,驗證失敗!") ;

objContext.Response.End() ;

}

string userid = "" ;

userid = objApp.Request["userid"].ToString() ;

string password = "" ;

password = objApp.Request["password"].ToString() ;

string[] strRoles ;

strRoles = AuthenticateAndGetRoles(userid, password) ;

if ((strRoles == null) || (strRoles.GetLength(0) == 0))

{

objContext.Response.Write("使用者名稱或密碼錯誤!") ;

objApp.CompleteRequest() ;//終止一個Http請求

}

GenericIdentity objIdentity = new GenericIdentity(userid,"CustomAuthentication") ;

objContext.User = new GenericPrincipal(objIdentity, strRoles) ;

}

private string[] AuthenticateAndGetRoles(string r_strUserID,string r_strPassword)

{

string[] strRoles = null ;

if ((r_strUserID.Equals("Zhangsan")) && (r_strPassword.Equals("111")))

{

strRoles = new String[1] ;

strRoles[0] = "Administrator" ;

}

else if ((r_strUserID.Equals("Lisi")) && (r_strPassword.Equals("222")))

{

strRoles = new string[1] ;

strRoles[0] = "User" ;

}

return strRoles ;

}

}

}

編譯一下,下邊做測試頁面,很簡單,放一個label,text=“測試頁面”如果成功則顯示測試頁面。然後在web.config裡面配置,這裡很重要。新增 注意註釋部分。

然後新增 這個節點,這個大家應該都能明白。

然後啟動測試頁面。剛啟動開始後頁面一定顯示“使用者名稱和密碼為空,驗證失敗!”呵呵,別忘記了咱們這就是目的,然後在你的位址列後邊新增?userid= Zhangsan&password=111這行字。然後就會顯示“測試頁面”這幾個字。大家可以多輸入幾個名字單步除錯一下就明白了。

WebConfig設定

<add type=“classname,assemblyname”

name=“modulename”/>

子標記說明:

將HttpModule 類新增到應用程式。請注意,如果以前已指定了相同的謂詞/路徑組合(例如在父目錄的Web.config 檔案中),則對的第二個呼叫將重寫以前的設定。

從應用程式移除HttpModule 類。

從應用程式移除所有HttpModule 對映。

深入研究HttpModule

HttpModule通過對HttpApplication物件的一系列事件的處理來對HTTP處理管道施加影響,這些事件在HttpModule的Init方法中進行註冊,包括:

BeginRequest

AuthenticateRequest

AuthorizeRequest

ResolveRequestCache

AcquireRequestState

PreRequestHandlerExecute

PostRequestHandlerExecute

ReleaseRequestState

UpdateRequestCache

EndRequest

我們都可以對以上事件進行重新定義,注意時重新定義不時覆蓋。我們看一個例子,多個HttpModule的實現,建立兩個類庫,什麼都相同就是類名不相同,引入相應的名稱空間後我們編寫這個類,程式碼如下:

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

namespace HttpModuleTest1

{

public class Test1Module:IHttpModule

{

public Test1Module()

{

}

public string ModuleName

{

get

{

return "Test1Module";

}

}

public void Init(HttpApplication application)

{

application.BeginRequest += new EventHandler(myBeginRequest);

application.EndRequest += new EventHandler(myEndRequest);

application.PreRequestHandlerExecute += new EventHandler(myPreRequestHandlerExecute);

application.PostRequestHandlerExecute += new EventHandler(myPostRequestHandlerExecute);

application.ReleaseRequestState += new EventHandler(myReleaseRequestState);

application.AcquireRequestState += new EventHandler(myAcquireRequestState);

application.AuthenticateRequest += new EventHandler(myAuthenticateRequest);

application.AuthorizeRequest += new EventHandler(myAuthorizeRequest);

application.ResolveRequestCache += new EventHandler(myResolveRequestCache);

application.PreSendRequestHeaders += new EventHandler(myPreSendRequestHeaders);

application.PreSendRequestContent += new EventHandler(myPreSendRequestContent);

}

private void myBeginRequest(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Beggining of Request

");

}

private void myEndRequest(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:End of Request

");

}

private void myPreRequestHandlerExecute(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_RequestHandlerExecute:

");

}

private void myPostRequestHandlerExecute(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_PostRequestHandlerExecute:

");

}

private void myReleaseRequestState(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_ReleaseRequestState:

");

}

private void myAcquireRequestState(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_ReleaseRequestState:

");

}

private void myAuthenticateRequest(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_AuthenticateRequest:

");

}

private void myAuthorizeRequest(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_AuthorizeRequest:

");

}

private void myResolveRequestCache(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_ResolveRequestCache:

");

}

private void myPreSendRequestHeaders(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_PreSendRequestHeaders:

");

}

private void myPreSendRequestContent(object source, EventArgs e)

{

HttpApplication application = (HttpApplication)source;

HttpContext context = application.Context;

context.Response.Write("Test1Module:Application_PreSendRequestContent:

");

}

public void Dispose()

{

}

}

}

然後在web.config裡新增,不明白的看註釋部分。

還是用剛才那個測試頁面,我們就可以觀察到兩個test的執行順序。

我們具體分析一下就是這樣的:

HttpRequest開始->進入HttpModule->HttpModule->

首次截獲HttpRequest->HttpModule.BeginRequest->

HttpModule.AuthorizeRequest->HttpModule.ResolveRequestCache->

初始化HttpHandler->建立HttpHandler控制點->HttpModule繼續處理。HttpHandler已經建立,此後Session可用->HttpModule.AcquireRequestState

->HttpModule.PreRequestHandlerExecute->進入HttpHandler處理HttpRequest

->HttpHandler->HttpHandler.ProcessRequest->返回HttpModule, HttpHandler結束,Session失效->HttpModule.PostRequestHandlerExecute-> HttpModule.ReleaseRequestState->

HttpModule.UpdateRequestCache->HttpModule.EndRequest->HttpModule.PreSendRequestHeaders->HttpModule.PreSendRequestContent->

將處理的資料返回客戶端,處理結束。

HttpHandler:

HttpHandler實現了ISAPI Extention的功能,他處理請求(Request)的資訊和傳送響應(Response)。HttpHandler功能的實現通過實現IHttpHandler介面來達到。

HTTP處理程式是實現了System.Web.IHttpHandler介面的.NET元件。任何實現了IHttpHandler介面的類都可以用於處理輸入的HTTP請求。HTTP處理程式與ISAPI擴充套件有些類似。HTTP處理程式和ISAPI擴充套件的差別在於在URL中可以使用HTTP處理程式的檔案名稱直接呼叫它們,與ISAPI擴充套件類似。

HttpHandler的實現,實現我們的HTTP處理程式包含以下步驟:

編寫一個實現IHttpHandler介面的類。

在web.config或machine.config檔案中註冊這個處理程式。

在Internet服務管理器中把檔案擴充套件(你想要處理的副檔名)對映到ASP.NETISAPI擴充套件DLL(aspnet_isapi.dll)上。

我們來看一個例子,開啟IIS伺服器,屬性,主目錄下有個配置,裡面你就可以找到你的程式所執行檔案所要呼叫的.dll檔案。我們可以看到.aspx就是 C:WINDOWSMicrosoft.NETFramework 2.0.50727aspnet_isapi.dll這個檔案

來執行的。這裡還可以新增你自己任意定義任意副檔名檔案,定義了後你的伺服器就可以認識這些人間,注意只是你的伺服器,別人的不認識。這時候大家就會對網路上流行的各式各樣的字尾名不奇怪了吧,可以自己定義的。

我們自己定義一個帶.xxx字尾的。

新增一個類庫,引用的相應的名稱空間,

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

namespace MyHandler

{

public class NewHandler:IHttpHandler

{

public NewHandler()

{

// TODO: 此處新增構造邏輯

}

Implementation of IHttpHandler#region Implementation of IHttpHandler

/**////

/// http處理程式的核心。我們呼叫這個方法來處理http請求。

///

///

public void ProcessRequest(System.Web.HttpContext context)

{

HttpResponse objResponse = context.Response;

objResponse.Write("

Hello xxx ! ");

objResponse.Write("");

}

/**////

/// 我們呼叫這個屬性來決定http處理程式的例項是否可以用於處理相同其它型別的請求。

/// HTTP處理程式可以返回true或false來表明它們是否可以重複使用。

///

public bool IsReusable

//

{

get

{

return true;

}

}

#endregion

}

}

然後再web.config裡面配置相應節點:這裡不懂的參考前邊的,XXX就是我們剛才定義那個字尾名。

<add verb="*" path="*.xxx"

type="MyHandler.NewHandler,MyHandler" />

然後新增一個測試頁面,就可以了。

HttpHandler之間的關係是這樣的:

傳送一個Http請求,然後判斷是否存在自定義的HttpHandler,如果存在的話由自定義的HttpHandler處理Http請求,否則由系統預設的HttpHandler處理Http請求。

在HttpHandler中訪問Session:

不能直接通過HttpContext訪問。

必須實現IRequiresSessionState介面。

IRequiresSessionState介面指定目標HTTP處理程式介面具有對會話狀態值的讀寫訪問許可權。這是一個標記介面,沒有任何方法。

怎樣實現呢,我們還是來看例子吧:新增類庫,引用相應的名稱空間。

using System;

using System.Web;

using System.Web.SessionState;

namespace MyHandler

{

public class NewHandlerSession : IHttpHandler,IRequiresSessionState

{

public NewHandlerSession()

{

// TODO: 此處新增構造邏輯

}

Implementation of IHttpHandler#region Implementation of IHttpHandler

/**////

/// http處理程式的核心。我們呼叫這個方法來處理http請求。

///

///

public void ProcessRequest(System.Web.HttpContext context)

{

HttpResponse objResponse = context.Response ;

HttpRequest objRequest = context.Request;

HttpSessionState objSession = context.Session;

objResponse.Write("歡迎使用自定義HttpHandler!

");

objSession["Test"] = "Session 測試!

";

objResponse.Write("Session的值為:"+objSession["Test"].ToString());

}

/**////

/// 我們呼叫這個屬性來決定http處理程式的例項是否可以用於處理相同其它型別的請求。

/// HTTP處理程式可以返回true或false來表明它們是否可以重複使用。

///

public bool IsReusable

//

{

get

{

return true;

}

}

#endregion

}

}

然後配置Web.config的節點:

<add verb="*" path="*"

type="MyHandler.NewHandlerSession,MyHandlerSession" />

這樣就可以了。

ASP.NET事件模型機制:

ASP.NET之所以對於以前的ASP是一個革命性的鉅變,在很大程度上是由於ASP.NET技術是一種完全基於事件驅動的全新技術。

在ASP.NET中事件的觸發和處理分別是在客戶端和伺服器段進行的。

ASP.NET中,如果頻繁和伺服器進行事件資訊的傳遞,會大大降低伺服器的處理效率和效能,因而有些事件如OnMouseOver沒有提供。

但提供了Change事件。為了提高效率它們被快取在客戶端。等到再一次事件資訊被髮送到伺服器端時一同傳送回去。