关于上下文
Susan Warren
Microsoft Corporation
編寫 Web 應(yīng)用程序時最常見的問題之一,是要讓代碼知道它的執(zhí)行上下文。讓我們通過一個簡單的例子(即個性化頁面)來說明這個問題:
請登錄。
與
歡迎 Susan!
雖然看起來很簡單,但即使是這一小段 Web UI,仍然需要好幾段信息,而且每次請求該頁時這些信息都會發(fā)生變化。我們需要知道以下內(nèi)容:
用戶登錄了嗎?
用戶的顯示名是什么?
更通常的問題是,每次請求該頁時,唯一的上下文是什么?以及如何編寫代碼以便能考慮到此信息?
事實上,由于 HTTP 的無狀態(tài)特性,Web 應(yīng)用程序可能需要跟蹤許多不同的上下文片段。當用戶與 Web 應(yīng)用程序交互時,瀏覽器將一系列獨立的 HTTP 請求發(fā)送到 Web 服務(wù)器。應(yīng)用程序自身必須將這些請求組織成令用戶感到愉悅的體驗;同時,知道請求的上下文也十分關(guān)鍵。
ASP 引入了幾個內(nèi)部對象,如 Request 和 Application,以便幫助跟蹤 HTTP 請求的上下文。ASP.NET 完成下一步驟,并將這些對象以及其他幾個與上下文有關(guān)的對象捆綁在一起,形成一個極為方便的內(nèi)部對象 Context。
Context 是 System.Web.HttpContext(英文)類型的對象。它作為 ASP.NET Page 類的屬性公開。也可以通過用戶控件和業(yè)務(wù)對象(下文中詳細介紹)獲得該對象。以下是 HttpContext 形成的對象的部分列表:
對象 說明
Application 值的關(guān)鍵字/值對集合,可由應(yīng)用程序的每個用戶訪問。Application 是 System.Web.HttpApplicationState 類型。
ApplicationInstance 實際運行的應(yīng)用程序,它公開一些請求處理事件。這些事件在 Global.asax、HttpHandler 或 HttpModule 中處理。
Cache ASP.NET Cache 對象,它提供對緩存的編程訪問。Rob Howard 的 ASP.NET Caching 專欄(英文)對緩存作了詳盡介紹。
Error 處理頁時遇到的第一個錯誤(如果有)。有關(guān)詳細信息,請參閱 Rob 撰寫的 Exception to the Rule, Part 1(英文)。
Items 關(guān)鍵字/值對集合,可以用來在參與處理同一請求的所有組件之間傳遞信息。Items 是 System.Collections.IDictionary 類型。
Request 有關(guān) HTTP 請求的信息,包括瀏覽器信息、Cookies 以及在窗體或查詢字符串中傳遞的值。Request 是 System.Web.HttpRequest 類型。
Response 用于創(chuàng)建 HTTP 響應(yīng)的設(shè)置和內(nèi)容。Response 是 System.Web.HttpResponse 類型。
Server 服務(wù)器是一個實用程序類,帶有一些有用的幫助器方法,包括 Server.Execute()、Server.MapPath() 和 Server.HtmlEncode()。Server 是 System.Web.HttpServerUtility 類型的對象。
Session 值的關(guān)鍵字/值對集合,可由應(yīng)用程序的單個用戶訪問。Session 是 System.Web.HttpSessionState 類型。
Trace ASP.NET 的 Trace 對象,提供對跟蹤功能的訪問。有關(guān)詳細信息,請參閱 Rob 撰寫的文章 Tracing(英文)。
User 當前用戶(如果已經(jīng)過身份驗證)的安全上下文。Context.User.Identity 是用戶的名稱。User 是 System.Security.Principle.IPrincipal 類型的對象。
如果您是一位 ASP 開發(fā)人員,那么對上面講述的部分對象應(yīng)不會感到陌生。雖然有一些改進,但大體而言,它們在 ASP.NET 中的作用與在 ASP 中是完全一樣的。
Context 基礎(chǔ)知識
Context 中的部分對象也已升級為 Page 中的頂級對象。例如,Page.Context.Response 和 Page.Response 指的是同一個對象,因此,以下代碼是等價的:
[Visual Basic? Web 窗體]
Response.Write ("您好")
Context.Response.Write ("你好")
[C# Web 窗體]
Response.Write ("您好");
Context.Response.Write ("你好");
還可以從業(yè)務(wù)對象使用 Context 對象。HttpContext.Current 是靜態(tài)屬性,可以很方便地返回當前請求的上下文。這在各種方法中都十分有用,下面僅列舉一個從業(yè)務(wù)類的緩存中檢索項目的簡單示例:
[Visual Basic]
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 獲取緩存中的數(shù)據(jù)集
Dim _data As DataSet = _context.Cache("MyDataSet")
[C#]
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 獲取緩存中的數(shù)據(jù)集
DataSet _data = _context.Cache("MyDataSet");
操作中的 Context
Context 對象為一些常見的 ASP.NET“如何…?”問題提供了答案。也許,說明此寶貴對象的價值的最好方法,就是在操作中將它展示出來。下面是一些我所知道的最巧妙的 Context 技巧。
我如何從自己的業(yè)務(wù)類中生成 ASP.NET 跟蹤語句?
回答:很簡單!使用 HttpContext.Current 獲取 Context 對象,然后調(diào)用 Context.Trace.Write()。
[Visual Basic]
Imports System
Imports System.Web
Namespace Context
' 演示從業(yè)務(wù)類中生成一個 ASP.NET
' 跟蹤語句。
Public Class TraceEmit
Public Sub SomeMethod()
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 使用上下文編寫跟蹤語句
_context.Trace.Write("在 TraceEmit.SomeMethod 中")
End Sub
End Class
End Namespace
[C#]
using System;
using System.Web;
namespace Context
{
// 演示從業(yè)務(wù)類中生成一個 ASP.NET
// 跟蹤語句。
public class TraceEmit
{
public void SomeMethod() {
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 使用上下文編寫跟蹤語句
_context.Trace.Write("在 TraceEmit.SomeMethod 中");
}
}
}
如何才能從業(yè)務(wù)類中訪問會話狀態(tài)值?
回答:很簡單!使用 HttpContext.Current 獲取 Context 對象,然后訪問 Context.Session。
[Visual Basic]
Imports System
Imports System.Web
Namespace Context
' 演示從業(yè)務(wù)類中訪問 ASP.NET 內(nèi)部
' 會話。
Public Class UseSession
Public Sub SomeMethod()
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 訪問內(nèi)部會話
Dim _value As Object = _context.Session("TheValue")
End Sub
End Class
End Namespace
[C#]
using System;
using System.Web;
namespace Context
{
// 演示從業(yè)務(wù)類中訪問 ASP.NET 內(nèi)部
// 會話
public class UseSession
{
public void SomeMethod() {
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 訪問內(nèi)部會話
object _value = _context.Session["TheValue"];
}
}
}
如何才能在應(yīng)用程序的每頁中添加標準頁眉和頁腳?
回答:處理應(yīng)用程序的 BeginRequest 和 EndRequest 事件,并使用 Context.Response.Write 生成頁眉和頁腳的 HTML。
從技術(shù)上講,可以在 HttpModule 中或通過使用 Global.asax 處理 BeginRequest 這樣的應(yīng)用程序。HttpModules 的編寫比較困難,而且正如本例所示,簡單應(yīng)用程序使用的功能通常不使用它。因此,我們使用應(yīng)用程序范圍的 Global.asax 文件。
與 ASP 頁一樣,一些固有的 ASP.NET 上下文已提升為 HttpApplication 類的屬性,其中的類表示 Global.asax 繼承類。我們不需要使用 HttpContext.Current 獲取對 Context 對象的引用;它在 Global.asax. 中已可用。
本例中,我將 <html> 和 <body> 標記以及一條水平線放入頁眉部分,而將另一條水平線及相應(yīng)的結(jié)束標記放入頁腳部分。頁腳還包含版權(quán)消息。運行結(jié)果應(yīng)如下圖所示:
圖 1:瀏覽器中呈現(xiàn)的標準頁眉和頁腳示例
這是一個簡單的示例,但您可以很容易地將它擴展,使其包含標準的頁眉與導(dǎo)航,或者僅輸出相應(yīng)的 <!-- #include ---> 語句。請注意,如果希望頁眉或頁腳包含交互內(nèi)容,應(yīng)考慮使用 ASP.NET 用戶控件。
[SomePage.aspx 源代碼 - 內(nèi)容示例]
<FONT face="Arial" color="#cc66cc" size="5">
常規(guī)頁面內(nèi)容
</FONT>
[Visual Basic Global.asax]
<%@ Application Language="VB" %>
<script runat="server">
Sub Application_BeginRequest(sender As Object, e As EventArgs)
' 生成頁眉
Context.Response.Write("<html>" + ControlChars.Lf + _
"<body bgcolor=#efefef>" + ControlChars.Lf + "<hr>" + _ ControlChars.Lf)
End Sub
Sub Application_EndRequest(sender As Object, e As EventArgs)
' 生成頁腳
Context.Response.Write("<hr>" + ControlChars.Lf + _
"2002 Microsoft Corporation 版權(quán)所有" + _
ControlChars.Lf + "</body>" + ControlChars.Lf + "</html>")
End Sub
</script>
[C# Global.asax]
<%@ Application Language="C#" %>
<script runat="server">
void Application_BeginRequest(Object sender, EventArgs e) {
// 生成頁眉
Context.Response.Write("<html>/n<body bgcolor=#efefef>/n<hr>/n");
}
void Application_EndRequest(Object sender, EventArgs e) {
// 生成頁腳
Context.Response.Write("<hr>/2002 Microsoft Corporation 版權(quán)所有/n");
Context.Response.Write("</body>/n</html>");
}
</script>
如何在用戶經(jīng)過身份驗證后顯示歡迎信息?
回答:測試 User 上下文對象以查看用戶是否經(jīng)過身份驗證。如果是,還要從 User 對象獲取用戶名。當然,這是本文開頭的示例。
[Visual Basic]
<script language="VB" runat="server">
Sub Page_Load(sender As Object, e As EventArgs) {
If User.Identity.IsAuthenticated Then
welcome.Text = "歡迎" + User.Identity.Name
Else
' 尚未登錄,添加一個指向登錄頁的鏈接
welcome.Text = "請登錄!"
welcome.NavigateUrl = "signin.aspx"
End If
End Sub
</script>
<asp:HyperLink id="welcome" runat="server" maintainstate="false">
</asp:HyperLink>
[C#]
<script language="C#" runat="server">
void Page_Load(object sender, EventArgs e) {
if (User.Identity.IsAuthenticated) {
welcome.Text = "歡迎" + User.Identity.Name;
}
else {
// 尚未登錄,添加一個指向登錄頁的鏈接
welcome.Text = "請登錄!";
welcome.NavigateUrl = "signin.aspx";
}
}
</script>
<asp:HyperLink id="welcome" runat="server" maintainstate="false">
</asp:HyperLink>
Context.Items 簡介
希望以上示例可以說明,使用手頭僅有的上下文信息編寫 Web 應(yīng)用程序是多么容易。那么,如果可以用同樣的方法訪問您應(yīng)用程序獨有的一些上下文,不是很好嗎?
這就是 Context.Items 集合的用途。它使用在參與處理請求的各部分代碼中都可用的方法,保存應(yīng)用程序的請求特有值。例如,同樣一條信息可以用在 Global.asax、ASPX 頁、頁內(nèi)的用戶控件中,也可以由頁調(diào)用的業(yè)務(wù)邏輯使用。
請考慮 IBuySpy Portal(英文)應(yīng)用程序示例。它使用一個簡單的主頁 DesktopDefault.aspx 來顯示門戶內(nèi)容。顯示的內(nèi)容取決于所選擇的選項卡,以及用戶(如果已經(jīng)過身份驗證)角色。
圖 2:IbuySpy 主頁
查詢字符串包含正被請求的選項卡的 TabIndedx 和 TabId 參數(shù)。在處理請求的整個過程中,一直使用此信息篩選要顯示給用戶的數(shù)據(jù)。http://www.ibuyspyportal.com/DesktopDefault.aspx?tabindex=1&tabid=2(英文)
要使用查詢字符串值,需要首先確保它是一個有效值,如果不是,則要進行一些錯誤處理。它并不是一大串代碼,但是您真的要在每個使用該值的頁和組件中復(fù)制它嗎?當然不!在 Portal 示例中,甚至更多的地方都涉及到它,因為一旦我們知道了 TabId,就可以預(yù)先加載其他信息。
Portal 使用查詢字符串值作為參數(shù),以構(gòu)造一個新的 PortalSettings 對象,并將它添加到 Global.asax 的 BeginRequest 事件的 Context.Items 中。由于在每個請求開始處都執(zhí)行了開始請求,這使得與該選項卡有關(guān)的值在應(yīng)用程序的所有頁和組件中都可用。請求完成后,對象將被自動丟棄 - 非常整齊!
[Visual Basic Global.asax]
Sub Application_BeginRequest(sender As [Object], e As EventArgs)
Dim tabIndex As Integer = 0
Dim tabId As Integer = 0
' 從查詢字符串獲取 TabIndex
If Not (Request.Params("tabindex") Is Nothing) Then
tabIndex = Int32.Parse(Request.Params("tabindex"))
End If
' 從查詢字符串獲取 TabID
If Not (Request.Params("tabid") Is Nothing) Then
tabId = Int32.Parse(Request.Params("tabid"))
End If
Context.Items.Add("PortalSettings", _
New PortalSettings(tabIndex, tabId))
End Sub
[C# Global.asax]
void Application_BeginRequest(Object sender, EventArgs e) {
int tabIndex = 0;
int tabId = 0;
// 從查詢字符串獲取 TabIndex
if (Request.Params["tabindex"] != null) {
tabIndex = Int32.Parse(Request.Params["tabindex"]);
}
// 從查詢字符串獲取 TabID
if (Request.Params["tabid"] != null) {
tabId = Int32.Parse(Request.Params["tabid"]);
}
Context.Items.Add("PortalSettings",
new PortalSettings(tabIndex, tabId));
}
DesktopPortalBanner.ascx 用戶控件從 Context 請求 PortalSetting 的對象,以訪問 Portal 的名稱和安全設(shè)置。事實上,此模塊是操作中的 Context 的一個典型綜合示例。為闡明這一點,我已將代碼進行了一些簡化,并用粗體標記了 HTTP 或應(yīng)用程序特定的 Context 被訪問過的所有地方。
[C# DesktopPortalBanner.ascx]
<%@ Import Namespace="ASPNetPortal" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script language="C#" runat="server">
public int tabIndex;
public bool ShowTabs = true;
protected String LogoffLink = "";
void Page_Load(Object sender, EventArgs e) {
// 從當前上下文獲取 PortalSettings
PortalSettings portalSettings =
(PortalSettings) Context.Items["PortalSettings"];
// 動態(tài)填充門戶站點名稱
siteName.Text = portalSettings.PortalName;
// 如果用戶已登錄,自定義歡迎信息
if (Request.IsAuthenticated == true) {
WelcomeMessage.Text = "歡迎" +
Context.User.Identity.Name + "!<" +
"span class=Accent" + ">|<" + "/span" + ">";
// 如果身份驗證模式為 Cookie,則提供一個注銷鏈接
if (Context.User.Identity.AuthenticationType == "Forms") {
LogoffLink = "<" + "span class=/"Accent/">|</span>/n" +
"<a href=" + Request.ApplicationPath +
"/Admin/Logoff.aspx class=SiteLink> 注銷" +
"</a>";
}
}
// 動態(tài)顯示門戶選項卡條
if (ShowTabs == true) {
tabIndex = portalSettings.ActiveTab.TabIndex;
// 生成要向用戶顯示的選項卡列表
ArrayList authorizedTabs = new ArrayList();
int addedTabs = 0;
for (int i=0; i < portalSettings.DesktopTabs.Count; i++) {
TabStripDetails tab =
(TabStripDetails)portalSettings.DesktopTabs[i];
if (PortalSecurity.IsInRoles(tab.AuthorizedRoles)) {
authorizedTabs.Add(tab);
}
if (addedTabs == tabIndex) {
tabs.SelectedIndex = addedTabs;
}
addedTabs++;
}
// 用已授權(quán)的選項卡填充頁頂部的選項卡
// 列表
tabs.DataSource = authorizedTabs;
tabs.DataBind();
}
}
</script>
<table width="100%" cellspacing="0" class="HeadBg" border="0">
<tr valign="top">
<td colspan="3" align="right">
<asp:label id="WelcomeMessage" runat="server" />
<a href="<%= Request.ApplicationPath %>">Portal 主頁</a>
<span class="Accent"> |</span>
<a href="<%= Request.ApplicationPath %>/Docs/Docs.htm">
Portal 文檔</a>
<%= LogoffLink %>
??
</td>
</tr>
<tr>
<td width="10" rowspan="2">
?
</td>
<td height="40">
<asp:label id="siteName" runat="server" />
</td>
<td align="center" rowspan="2">
?
</td>
</tr>
<tr>
<td>
<asp:datalist id="tabs" runat="server">
<ItemTemplate>
?
<a href='<%= Request.ApplicationPath %>
/DesktopDefault.aspx?tabindex=<%# Container.ItemIndex %>&tabid=
<%# ((TabStripDetails) Container.DataItem).TabId %>'>
<%# ((TabStripDetails) Container.DataItem).TabName %>
</a>?
</ItemTemplate>
<SelectedItemTemplate>
?
<span class="SelectedTab">
<%# ((TabStripDetails) Container.DataItem).TabName %>
</span>?
</SelectedItemTemplate>
</asp:datalist>
</td>
</tr>
</table>
您可以使用 Visual Basic 和 C# 在 http://www.ibuyspy.com(英文)聯(lián)機瀏覽并運行 IBuySpy Portal 的完整源文件,或者下載后再運行。
小結(jié)
Context 是 ASP.NET 中的又一個“精益求精”的功能。它擴展了 ASP 的已經(jīng)很不錯的上下文支持,以便將兩個掛鉤添加到 ASP.NET 的新運行時功能中。同時添加了 Context.Items,作為短期值的新狀態(tài)機制。但對于開發(fā)人員,此功能的最大好處是使代碼更緊湊,且易于維護,而且此上下文我們都能看懂。
Microsoft Corporation
編寫 Web 應(yīng)用程序時最常見的問題之一,是要讓代碼知道它的執(zhí)行上下文。讓我們通過一個簡單的例子(即個性化頁面)來說明這個問題:
請登錄。
與
歡迎 Susan!
雖然看起來很簡單,但即使是這一小段 Web UI,仍然需要好幾段信息,而且每次請求該頁時這些信息都會發(fā)生變化。我們需要知道以下內(nèi)容:
用戶登錄了嗎?
用戶的顯示名是什么?
更通常的問題是,每次請求該頁時,唯一的上下文是什么?以及如何編寫代碼以便能考慮到此信息?
事實上,由于 HTTP 的無狀態(tài)特性,Web 應(yīng)用程序可能需要跟蹤許多不同的上下文片段。當用戶與 Web 應(yīng)用程序交互時,瀏覽器將一系列獨立的 HTTP 請求發(fā)送到 Web 服務(wù)器。應(yīng)用程序自身必須將這些請求組織成令用戶感到愉悅的體驗;同時,知道請求的上下文也十分關(guān)鍵。
ASP 引入了幾個內(nèi)部對象,如 Request 和 Application,以便幫助跟蹤 HTTP 請求的上下文。ASP.NET 完成下一步驟,并將這些對象以及其他幾個與上下文有關(guān)的對象捆綁在一起,形成一個極為方便的內(nèi)部對象 Context。
Context 是 System.Web.HttpContext(英文)類型的對象。它作為 ASP.NET Page 類的屬性公開。也可以通過用戶控件和業(yè)務(wù)對象(下文中詳細介紹)獲得該對象。以下是 HttpContext 形成的對象的部分列表:
對象 說明
Application 值的關(guān)鍵字/值對集合,可由應(yīng)用程序的每個用戶訪問。Application 是 System.Web.HttpApplicationState 類型。
ApplicationInstance 實際運行的應(yīng)用程序,它公開一些請求處理事件。這些事件在 Global.asax、HttpHandler 或 HttpModule 中處理。
Cache ASP.NET Cache 對象,它提供對緩存的編程訪問。Rob Howard 的 ASP.NET Caching 專欄(英文)對緩存作了詳盡介紹。
Error 處理頁時遇到的第一個錯誤(如果有)。有關(guān)詳細信息,請參閱 Rob 撰寫的 Exception to the Rule, Part 1(英文)。
Items 關(guān)鍵字/值對集合,可以用來在參與處理同一請求的所有組件之間傳遞信息。Items 是 System.Collections.IDictionary 類型。
Request 有關(guān) HTTP 請求的信息,包括瀏覽器信息、Cookies 以及在窗體或查詢字符串中傳遞的值。Request 是 System.Web.HttpRequest 類型。
Response 用于創(chuàng)建 HTTP 響應(yīng)的設(shè)置和內(nèi)容。Response 是 System.Web.HttpResponse 類型。
Server 服務(wù)器是一個實用程序類,帶有一些有用的幫助器方法,包括 Server.Execute()、Server.MapPath() 和 Server.HtmlEncode()。Server 是 System.Web.HttpServerUtility 類型的對象。
Session 值的關(guān)鍵字/值對集合,可由應(yīng)用程序的單個用戶訪問。Session 是 System.Web.HttpSessionState 類型。
Trace ASP.NET 的 Trace 對象,提供對跟蹤功能的訪問。有關(guān)詳細信息,請參閱 Rob 撰寫的文章 Tracing(英文)。
User 當前用戶(如果已經(jīng)過身份驗證)的安全上下文。Context.User.Identity 是用戶的名稱。User 是 System.Security.Principle.IPrincipal 類型的對象。
如果您是一位 ASP 開發(fā)人員,那么對上面講述的部分對象應(yīng)不會感到陌生。雖然有一些改進,但大體而言,它們在 ASP.NET 中的作用與在 ASP 中是完全一樣的。
Context 基礎(chǔ)知識
Context 中的部分對象也已升級為 Page 中的頂級對象。例如,Page.Context.Response 和 Page.Response 指的是同一個對象,因此,以下代碼是等價的:
[Visual Basic? Web 窗體]
Response.Write ("您好")
Context.Response.Write ("你好")
[C# Web 窗體]
Response.Write ("您好");
Context.Response.Write ("你好");
還可以從業(yè)務(wù)對象使用 Context 對象。HttpContext.Current 是靜態(tài)屬性,可以很方便地返回當前請求的上下文。這在各種方法中都十分有用,下面僅列舉一個從業(yè)務(wù)類的緩存中檢索項目的簡單示例:
[Visual Basic]
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 獲取緩存中的數(shù)據(jù)集
Dim _data As DataSet = _context.Cache("MyDataSet")
[C#]
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 獲取緩存中的數(shù)據(jù)集
DataSet _data = _context.Cache("MyDataSet");
操作中的 Context
Context 對象為一些常見的 ASP.NET“如何…?”問題提供了答案。也許,說明此寶貴對象的價值的最好方法,就是在操作中將它展示出來。下面是一些我所知道的最巧妙的 Context 技巧。
我如何從自己的業(yè)務(wù)類中生成 ASP.NET 跟蹤語句?
回答:很簡單!使用 HttpContext.Current 獲取 Context 對象,然后調(diào)用 Context.Trace.Write()。
[Visual Basic]
Imports System
Imports System.Web
Namespace Context
' 演示從業(yè)務(wù)類中生成一個 ASP.NET
' 跟蹤語句。
Public Class TraceEmit
Public Sub SomeMethod()
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 使用上下文編寫跟蹤語句
_context.Trace.Write("在 TraceEmit.SomeMethod 中")
End Sub
End Class
End Namespace
[C#]
using System;
using System.Web;
namespace Context
{
// 演示從業(yè)務(wù)類中生成一個 ASP.NET
// 跟蹤語句。
public class TraceEmit
{
public void SomeMethod() {
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 使用上下文編寫跟蹤語句
_context.Trace.Write("在 TraceEmit.SomeMethod 中");
}
}
}
如何才能從業(yè)務(wù)類中訪問會話狀態(tài)值?
回答:很簡單!使用 HttpContext.Current 獲取 Context 對象,然后訪問 Context.Session。
[Visual Basic]
Imports System
Imports System.Web
Namespace Context
' 演示從業(yè)務(wù)類中訪問 ASP.NET 內(nèi)部
' 會話。
Public Class UseSession
Public Sub SomeMethod()
' 獲取請求上下文
Dim _context As HttpContext = HttpContext.Current
' 訪問內(nèi)部會話
Dim _value As Object = _context.Session("TheValue")
End Sub
End Class
End Namespace
[C#]
using System;
using System.Web;
namespace Context
{
// 演示從業(yè)務(wù)類中訪問 ASP.NET 內(nèi)部
// 會話
public class UseSession
{
public void SomeMethod() {
// 獲取請求上下文
HttpContext _context = HttpContext.Current;
// 訪問內(nèi)部會話
object _value = _context.Session["TheValue"];
}
}
}
如何才能在應(yīng)用程序的每頁中添加標準頁眉和頁腳?
回答:處理應(yīng)用程序的 BeginRequest 和 EndRequest 事件,并使用 Context.Response.Write 生成頁眉和頁腳的 HTML。
從技術(shù)上講,可以在 HttpModule 中或通過使用 Global.asax 處理 BeginRequest 這樣的應(yīng)用程序。HttpModules 的編寫比較困難,而且正如本例所示,簡單應(yīng)用程序使用的功能通常不使用它。因此,我們使用應(yīng)用程序范圍的 Global.asax 文件。
與 ASP 頁一樣,一些固有的 ASP.NET 上下文已提升為 HttpApplication 類的屬性,其中的類表示 Global.asax 繼承類。我們不需要使用 HttpContext.Current 獲取對 Context 對象的引用;它在 Global.asax. 中已可用。
本例中,我將 <html> 和 <body> 標記以及一條水平線放入頁眉部分,而將另一條水平線及相應(yīng)的結(jié)束標記放入頁腳部分。頁腳還包含版權(quán)消息。運行結(jié)果應(yīng)如下圖所示:
圖 1:瀏覽器中呈現(xiàn)的標準頁眉和頁腳示例
這是一個簡單的示例,但您可以很容易地將它擴展,使其包含標準的頁眉與導(dǎo)航,或者僅輸出相應(yīng)的 <!-- #include ---> 語句。請注意,如果希望頁眉或頁腳包含交互內(nèi)容,應(yīng)考慮使用 ASP.NET 用戶控件。
[SomePage.aspx 源代碼 - 內(nèi)容示例]
<FONT face="Arial" color="#cc66cc" size="5">
常規(guī)頁面內(nèi)容
</FONT>
[Visual Basic Global.asax]
<%@ Application Language="VB" %>
<script runat="server">
Sub Application_BeginRequest(sender As Object, e As EventArgs)
' 生成頁眉
Context.Response.Write("<html>" + ControlChars.Lf + _
"<body bgcolor=#efefef>" + ControlChars.Lf + "<hr>" + _ ControlChars.Lf)
End Sub
Sub Application_EndRequest(sender As Object, e As EventArgs)
' 生成頁腳
Context.Response.Write("<hr>" + ControlChars.Lf + _
"2002 Microsoft Corporation 版權(quán)所有" + _
ControlChars.Lf + "</body>" + ControlChars.Lf + "</html>")
End Sub
</script>
[C# Global.asax]
<%@ Application Language="C#" %>
<script runat="server">
void Application_BeginRequest(Object sender, EventArgs e) {
// 生成頁眉
Context.Response.Write("<html>/n<body bgcolor=#efefef>/n<hr>/n");
}
void Application_EndRequest(Object sender, EventArgs e) {
// 生成頁腳
Context.Response.Write("<hr>/2002 Microsoft Corporation 版權(quán)所有/n");
Context.Response.Write("</body>/n</html>");
}
</script>
如何在用戶經(jīng)過身份驗證后顯示歡迎信息?
回答:測試 User 上下文對象以查看用戶是否經(jīng)過身份驗證。如果是,還要從 User 對象獲取用戶名。當然,這是本文開頭的示例。
[Visual Basic]
<script language="VB" runat="server">
Sub Page_Load(sender As Object, e As EventArgs) {
If User.Identity.IsAuthenticated Then
welcome.Text = "歡迎" + User.Identity.Name
Else
' 尚未登錄,添加一個指向登錄頁的鏈接
welcome.Text = "請登錄!"
welcome.NavigateUrl = "signin.aspx"
End If
End Sub
</script>
<asp:HyperLink id="welcome" runat="server" maintainstate="false">
</asp:HyperLink>
[C#]
<script language="C#" runat="server">
void Page_Load(object sender, EventArgs e) {
if (User.Identity.IsAuthenticated) {
welcome.Text = "歡迎" + User.Identity.Name;
}
else {
// 尚未登錄,添加一個指向登錄頁的鏈接
welcome.Text = "請登錄!";
welcome.NavigateUrl = "signin.aspx";
}
}
</script>
<asp:HyperLink id="welcome" runat="server" maintainstate="false">
</asp:HyperLink>
Context.Items 簡介
希望以上示例可以說明,使用手頭僅有的上下文信息編寫 Web 應(yīng)用程序是多么容易。那么,如果可以用同樣的方法訪問您應(yīng)用程序獨有的一些上下文,不是很好嗎?
這就是 Context.Items 集合的用途。它使用在參與處理請求的各部分代碼中都可用的方法,保存應(yīng)用程序的請求特有值。例如,同樣一條信息可以用在 Global.asax、ASPX 頁、頁內(nèi)的用戶控件中,也可以由頁調(diào)用的業(yè)務(wù)邏輯使用。
請考慮 IBuySpy Portal(英文)應(yīng)用程序示例。它使用一個簡單的主頁 DesktopDefault.aspx 來顯示門戶內(nèi)容。顯示的內(nèi)容取決于所選擇的選項卡,以及用戶(如果已經(jīng)過身份驗證)角色。
圖 2:IbuySpy 主頁
查詢字符串包含正被請求的選項卡的 TabIndedx 和 TabId 參數(shù)。在處理請求的整個過程中,一直使用此信息篩選要顯示給用戶的數(shù)據(jù)。http://www.ibuyspyportal.com/DesktopDefault.aspx?tabindex=1&tabid=2(英文)
要使用查詢字符串值,需要首先確保它是一個有效值,如果不是,則要進行一些錯誤處理。它并不是一大串代碼,但是您真的要在每個使用該值的頁和組件中復(fù)制它嗎?當然不!在 Portal 示例中,甚至更多的地方都涉及到它,因為一旦我們知道了 TabId,就可以預(yù)先加載其他信息。
Portal 使用查詢字符串值作為參數(shù),以構(gòu)造一個新的 PortalSettings 對象,并將它添加到 Global.asax 的 BeginRequest 事件的 Context.Items 中。由于在每個請求開始處都執(zhí)行了開始請求,這使得與該選項卡有關(guān)的值在應(yīng)用程序的所有頁和組件中都可用。請求完成后,對象將被自動丟棄 - 非常整齊!
[Visual Basic Global.asax]
Sub Application_BeginRequest(sender As [Object], e As EventArgs)
Dim tabIndex As Integer = 0
Dim tabId As Integer = 0
' 從查詢字符串獲取 TabIndex
If Not (Request.Params("tabindex") Is Nothing) Then
tabIndex = Int32.Parse(Request.Params("tabindex"))
End If
' 從查詢字符串獲取 TabID
If Not (Request.Params("tabid") Is Nothing) Then
tabId = Int32.Parse(Request.Params("tabid"))
End If
Context.Items.Add("PortalSettings", _
New PortalSettings(tabIndex, tabId))
End Sub
[C# Global.asax]
void Application_BeginRequest(Object sender, EventArgs e) {
int tabIndex = 0;
int tabId = 0;
// 從查詢字符串獲取 TabIndex
if (Request.Params["tabindex"] != null) {
tabIndex = Int32.Parse(Request.Params["tabindex"]);
}
// 從查詢字符串獲取 TabID
if (Request.Params["tabid"] != null) {
tabId = Int32.Parse(Request.Params["tabid"]);
}
Context.Items.Add("PortalSettings",
new PortalSettings(tabIndex, tabId));
}
DesktopPortalBanner.ascx 用戶控件從 Context 請求 PortalSetting 的對象,以訪問 Portal 的名稱和安全設(shè)置。事實上,此模塊是操作中的 Context 的一個典型綜合示例。為闡明這一點,我已將代碼進行了一些簡化,并用粗體標記了 HTTP 或應(yīng)用程序特定的 Context 被訪問過的所有地方。
[C# DesktopPortalBanner.ascx]
<%@ Import Namespace="ASPNetPortal" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script language="C#" runat="server">
public int tabIndex;
public bool ShowTabs = true;
protected String LogoffLink = "";
void Page_Load(Object sender, EventArgs e) {
// 從當前上下文獲取 PortalSettings
PortalSettings portalSettings =
(PortalSettings) Context.Items["PortalSettings"];
// 動態(tài)填充門戶站點名稱
siteName.Text = portalSettings.PortalName;
// 如果用戶已登錄,自定義歡迎信息
if (Request.IsAuthenticated == true) {
WelcomeMessage.Text = "歡迎" +
Context.User.Identity.Name + "!<" +
"span class=Accent" + ">|<" + "/span" + ">";
// 如果身份驗證模式為 Cookie,則提供一個注銷鏈接
if (Context.User.Identity.AuthenticationType == "Forms") {
LogoffLink = "<" + "span class=/"Accent/">|</span>/n" +
"<a href=" + Request.ApplicationPath +
"/Admin/Logoff.aspx class=SiteLink> 注銷" +
"</a>";
}
}
// 動態(tài)顯示門戶選項卡條
if (ShowTabs == true) {
tabIndex = portalSettings.ActiveTab.TabIndex;
// 生成要向用戶顯示的選項卡列表
ArrayList authorizedTabs = new ArrayList();
int addedTabs = 0;
for (int i=0; i < portalSettings.DesktopTabs.Count; i++) {
TabStripDetails tab =
(TabStripDetails)portalSettings.DesktopTabs[i];
if (PortalSecurity.IsInRoles(tab.AuthorizedRoles)) {
authorizedTabs.Add(tab);
}
if (addedTabs == tabIndex) {
tabs.SelectedIndex = addedTabs;
}
addedTabs++;
}
// 用已授權(quán)的選項卡填充頁頂部的選項卡
// 列表
tabs.DataSource = authorizedTabs;
tabs.DataBind();
}
}
</script>
<table width="100%" cellspacing="0" class="HeadBg" border="0">
<tr valign="top">
<td colspan="3" align="right">
<asp:label id="WelcomeMessage" runat="server" />
<a href="<%= Request.ApplicationPath %>">Portal 主頁</a>
<span class="Accent"> |</span>
<a href="<%= Request.ApplicationPath %>/Docs/Docs.htm">
Portal 文檔</a>
<%= LogoffLink %>
??
</td>
</tr>
<tr>
<td width="10" rowspan="2">
?
</td>
<td height="40">
<asp:label id="siteName" runat="server" />
</td>
<td align="center" rowspan="2">
?
</td>
</tr>
<tr>
<td>
<asp:datalist id="tabs" runat="server">
<ItemTemplate>
?
<a href='<%= Request.ApplicationPath %>
/DesktopDefault.aspx?tabindex=<%# Container.ItemIndex %>&tabid=
<%# ((TabStripDetails) Container.DataItem).TabId %>'>
<%# ((TabStripDetails) Container.DataItem).TabName %>
</a>?
</ItemTemplate>
<SelectedItemTemplate>
?
<span class="SelectedTab">
<%# ((TabStripDetails) Container.DataItem).TabName %>
</span>?
</SelectedItemTemplate>
</asp:datalist>
</td>
</tr>
</table>
您可以使用 Visual Basic 和 C# 在 http://www.ibuyspy.com(英文)聯(lián)機瀏覽并運行 IBuySpy Portal 的完整源文件,或者下載后再運行。
小結(jié)
Context 是 ASP.NET 中的又一個“精益求精”的功能。它擴展了 ASP 的已經(jīng)很不錯的上下文支持,以便將兩個掛鉤添加到 ASP.NET 的新運行時功能中。同時添加了 Context.Items,作為短期值的新狀態(tài)機制。但對于開發(fā)人員,此功能的最大好處是使代碼更緊湊,且易于維護,而且此上下文我們都能看懂。
總結(jié)
- 上一篇: 给DataGrid添加确定删除的功能
- 下一篇: ASP.NET ViewState 初探