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,作为短期值的新状态机制。但对于开发人员,此功能的最大好处是使代码更紧凑,且易于维护,而且此上下文我们都能看懂。

  • 相关阅读:
    Jasper_crosstab_Parameter_Crosstab Header
    Jasper_style
    Linux_hadoop_install
    Linux_jdk path (execute and install)
    Linux_install mod_ssl openssl apache
    Linux_install jdk
    Linux_service cloudera-scm-server start failed
    Linux_ERROR 1045 (28000): Access denied for user 'root'@'localhost'
    Jasper_table_Cloud not resolve style(s)
    Linux_shell条件判断if中的-a到-z的意思
  • 原文地址:https://www.cnblogs.com/wzyexf/p/353650.html
Copyright © 2011-2022 走看看