zoukankan      html  css  js  c++  java
  • 关于上下文

    Susan Warren
    Microsoft Corporation
    2002年1月14日

    编写 Web 应用程序时最常见的问题之一,是要让代码知道它的执行上下文。让我们通过一个简单的例子(即个性化页面)来说明这个问题:

         请登录。

         欢迎 Susan!

    虽然看起来很简单,但即使是这一小段 Web UI,仍然需要好几段信息,而且每次请求该页时这些信息都会发生变化。我们需要知道以下内容:

    1. 用户登录了吗?
    2. 用户的显示名是什么?

    更通常的问题是,每次请求该页时,唯一的上下文是什么?以及如何编写代码以便能考虑到此信息?

    事实上,由于 HTTP 的无状态特性,Web 应用程序可能需要跟踪许多不同的上下文片段。当用户与 Web 应用程序交互时,浏览器将一系列独立的 HTTP 请求发送到 Web 服务器。应用程序自身必须将这些请求组织成令用户感到愉悦的体验;同时,知道请求的上下文也十分关键。

    ASP 引入了几个内部对象,如 RequestApplication,以便帮助跟踪 HTTP 请求的上下文。ASP.NET 完成下一步骤,并将这些对象以及其他几个与上下文有关的对象捆绑在一起,形成一个极为方便的内部对象 Context

    Context System.Web.HttpContext(英文)类型的对象。它作为 ASP.NET Page 类的属性公开。也可以通过用户控件和业务对象(下文中详细介绍)获得该对象。以下是 HttpContext 形成的对象的部分列表:

    对象 说明
    Application 值的关键字/值对集合,可由应用程序的每个用户访问。Application 是 System.Web.HttpApplicationState 类型。
    ApplicationInstance 实际运行的应用程序,它公开一些请求处理事件。这些事件在 Global.asax、HttpHandler 或 HttpModule 中处理。
    Cache ASP.NET Cache 对象,它提供对缓存的编程访问。Rob Howard 的 ASP.NET Caching 专栏(英文)对缓存作了详尽介绍。
    Error 处理页时遇到的第一个错误(如果有)。有关详细信息,请参阅 Rob 撰写的 Exception to the Rule, Part 1(英文)。
    Items 关键字/值对集合,可以用来在参与处理同一请求的所有组件之间传递信息。Items 是 System.Collections.IDictionary 类型。
    Request 有关 HTTP 请求的信息,包括浏览器信息、Cookies 以及在窗体或查询字符串中传递的值。Request 是 System.Web.HttpRequest 类型。
    Response 用于创建 HTTP 响应的设置和内容。Response 是 System.Web.HttpResponse 类型。
    Server 服务器是一个实用程序类,带有一些有用的帮助器方法,包括 Server.Execute()Server.MapPath()Server.HtmlEncode()。Server 是 System.Web.HttpServerUtility 类型的对象。
    Session 值的关键字/值对集合,可由应用程序的单个用户访问。Session 是 System.Web.HttpSessionState 类型。
    Trace ASP.NET 的 Trace 对象,提供对跟踪功能的访问。有关详细信息,请参阅 Rob 撰写的文章 Tracing(英文)。
    User 当前用户(如果已经过身份验证)的安全上下文。Context.User.Identity 是用户的名称。User 是 System.Security.Principle.IPrincipal 类型的对象。

    如果您是一位 ASP 开发人员,那么对上面讲述的部分对象应不会感到陌生。虽然有一些改进,但大体而言,它们在 ASP.NET 中的作用与在 ASP 中是完全一样的。

    Context 基础知识

    Context 中的部分对象也已升级为 Page 中的顶级对象。例如,Page.Context.ResponsePage.Response 指的是同一个对象,因此,以下代码是等价的:

    [Visual Basic® Web 窗体]

       Response.Write ("您好")
    Context.Response.Write ("你好")
    

    [C# Web 窗体]

       Response.Write ("您好");
    Context.Response.Write ("你好");
    

    还可以从业务对象使用 Context 对象。HttpContext.Current 是静态属性,可以很方便地返回当前请求的上下文。这在各种方法中都十分有用,下面仅列举一个从业务类的缓存中检索项目的简单示例:

    [Visual Basic]

          ' 获取请求上下文
    Dim _context As HttpContext = HttpContext.Current
    ' 获取缓存中的数据集
    Dim _data As DataSet = _context.Cache("MyDataSet")
    

    [C#]

          // 获取请求上下文
    HttpContext _context = HttpContext.Current;
    // 获取缓存中的数据集
    DataSet _data = _context.Cache("MyDataSet");
    

    操作中的 Context

    Context 对象为一些常见的 ASP.NET“如何…?”问题提供了答案。也许,说明此宝贵对象的价值的最好方法,就是在操作中将它展示出来。下面是一些我所知道的最巧妙的 Context 技巧。

    我如何从自己的业务类中生成 ASP.NET 跟踪语句?

    回答:很简单!使用 HttpContext.Current 获取 Context 对象,然后调用 Context.Trace.Write()

    [Visual Basic]

    Imports System
    Imports System.Web
    Namespace Context
    ' 演示从业务类中生成一个 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
    {
    // 演示从业务类中生成一个 ASP.NET
    // 跟踪语句。
    public class TraceEmit
    {
    public void SomeMethod() {
    // 获取请求上下文
    HttpContext _context = HttpContext.Current;
    // 使用上下文编写跟踪语句
    _context.Trace.Write("在 TraceEmit.SomeMethod 中");
    }
    }
    }
    

    如何才能从业务类中访问会话状态值?

    回答:很简单!使用 HttpContext.Current 获取 Context 对象,然后访问 Context.Session

    [Visual Basic]

    Imports System
    Imports System.Web
    Namespace Context
    ' 演示从业务类中访问 ASP.NET 内部
    ' 会话。
    Public Class UseSession
    Public Sub SomeMethod()
    ' 获取请求上下文
    Dim _context As HttpContext = HttpContext.Current
    ' 访问内部会话
    Dim _value As Object = _context.Session("TheValue")
    End Sub
    End Class
    End Namespace
    

    [C#]

    using System;
    using System.Web;
    namespace Context
    {
    // 演示从业务类中访问 ASP.NET 内部
    // 会话
    public class UseSession
    {
    public void SomeMethod() {
    // 获取请求上下文
    HttpContext _context = HttpContext.Current;
    // 访问内部会话
    object _value = _context.Session["TheValue"];
    }
    }
    }
    

    如何才能在应用程序的每页中添加标准页眉和页脚?

    回答:处理应用程序的 BeginRequestEndRequest 事件,并使用 Context.Response.Write 生成页眉和页脚的 HTML。

    从技术上讲,可以在 HttpModule 中或通过使用 Global.asax 处理 BeginRequest 这样的应用程序。HttpModules 的编写比较困难,而且正如本例所示,简单应用程序使用的功能通常不使用它。因此,我们使用应用程序范围的 Global.asax 文件。

    与 ASP 页一样,一些固有的 ASP.NET 上下文已提升为 HttpApplication 类的属性,其中的类表示 Global.asax 继承类。我们不需要使用 HttpContext.Current 获取对 Context 对象的引用;它在 Global.asax. 中已可用。

    本例中,我将 <html><body> 标记以及一条水平线放入页眉部分,而将另一条水平线及相应的结束标记放入页脚部分。页脚还包含版权消息。运行结果应如下图所示:

    图 1:浏览器中呈现的标准页眉和页脚示例

    这是一个简单的示例,但您可以很容易地将它扩展,使其包含标准的页眉与导航,或者仅输出相应的 <!-- #include ---> 语句。请注意,如果希望页眉或页脚包含交互内容,应考虑使用 ASP.NET 用户控件。

    [SomePage.aspx 源代码 - 内容示例]

    <FONT face="Arial" color="#cc66cc" size="5">
    常规页面内容
    </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 版权所有" + _
    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 版权所有\n");
    Context.Response.Write("</body>\n</html>");
    }
    </script>
    

    如何在用户经过身份验证后显示欢迎信息?

    回答:测试 User 上下文对象以查看用户是否经过身份验证。如果是,还要从 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 应用程序是多么容易。那么,如果可以用同样的方法访问您应用程序独有的一些上下文,不是很好吗?

    这就是 Context.Items 集合的用途。它使用在参与处理请求的各部分代码中都可用的方法,保存应用程序的请求特有值。例如,同样一条信息可以用在 Global.asax、ASPX 页、页内的用户控件中,也可以由页调用的业务逻辑使用。

    请考虑 IBuySpy Portal(英文)应用程序示例。它使用一个简单的主页 DesktopDefault.aspx 来显示门户内容。显示的内容取决于所选择的选项卡,以及用户(如果已经过身份验证)角色。

    图 2:IbuySpy 主页

    查询字符串包含正被请求的选项卡的 TabIndedx 和 TabId 参数。在处理请求的整个过程中,一直使用此信息筛选要显示给用户的数据。http://www.ibuyspyportal.com/DesktopDefault.aspx?tabindex=1&tabid=2(英文)

    要使用查询字符串值,需要首先确保它是一个有效值,如果不是,则要进行一些错误处理。它并不是一大串代码,但是您真的要在每个使用该值的页和组件中复制它吗?当然不!在 Portal 示例中,甚至更多的地方都涉及到它,因为一旦我们知道了 TabId,就可以预先加载其他信息。

    Portal 使用查询字符串值作为参数,以构造一个新的 PortalSettings 对象,并将它添加到 Global.asax 的 BeginRequest 事件的 Context.Items 中。由于在每个请求开始处都执行了开始请求,这使得与该选项卡有关的值在应用程序的所有页和组件中都可用。请求完成后,对象将被自动丢弃 - 非常整齐!

    [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 的名称和安全设置。事实上,此模块是操作中的 Context 的一个典型综合示例。为阐明这一点,我已将代码进行了一些简化,并用粗体标记了 HTTP 或应用程序特定的 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"];
    // 动态填充门户站点名称
    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>";
    }
    }
    // 动态显示门户选项卡条
    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++;
    }
    // 用已授权的选项卡填充页顶部的选项卡
    // 列表
    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 %>
    &nbsp;&nbsp;
    </td>
    </tr>
    <tr>
    <td width="10" rowspan="2">
    &nbsp;
    </td>
    <td height="40">
    <asp:label id="siteName" runat="server" />
    </td>
    <td align="center" rowspan="2">
    &nbsp;
    </td>
    </tr>
    <tr>
    <td>
    <asp:datalist id="tabs" runat="server">
    <ItemTemplate>
    &nbsp;
    <a href='<%= Request.ApplicationPath %>
    /DesktopDefault.aspx?tabindex=<%# Container.ItemIndex %>&tabid=
    <%# ((TabStripDetails) Container.DataItem).TabId %>'>
    <%# ((TabStripDetails) Container.DataItem).TabName %>
    </a>&nbsp;
    </ItemTemplate>
    <SelectedItemTemplate>
    &nbsp;
    <span class="SelectedTab">
    <%# ((TabStripDetails) Container.DataItem).TabName %>
    </span>&nbsp;
    </SelectedItemTemplate>
    </asp:datalist>
    </td>
    </tr>
    </table>
    

    您可以使用 Visual Basic 和 C# 在 http://www.ibuyspy.com(英文)联机浏览并运行 IBuySpy Portal 的完整源文件,或者下载后再运行。

    小结

    Context 是 ASP.NET 中的又一个“精益求精”的功能。它扩展了 ASP 的已经很不错的上下文支持,以便将两个挂钩添加到 ASP.NET 的新运行时功能中。同时添加了 Context.Items,作为短期值的新状态机制。但对于开发人员,此功能的最大好处是使代码更紧凑,且易于维护,而且此上下文我们都能看懂。

  • 相关阅读:
    To select the file to upload we can use the standard HTML input control of type
    Cascading Menu Script using Javascript Explained
    网站首页head区代码规范
    轻松掌握 Java 泛型
    JDK 5.0 中的泛型类型学习
    如何在firefox下获取下列框选中option的text
    是同步方法还是 synchronized 代码? 详解多线程同步规则
    javascript select option对象总结
    Select的动态取值(Text,value),添加,删除。兼容IE,FireFox
    javascript在ie和firefox下的一些差异
  • 原文地址:https://www.cnblogs.com/wzyexf/p/353650.html
Copyright © 2011-2022 走看看