다음을 통해 공유


컨텍스트 문제

 

수잔 워렌
Microsoft Corporation

2002년 1월 14일

웹 애플리케이션 작성과 관련된 가장 일반적인 문제 중 하나는 코드가 실행 중인 컨텍스트를 알려주는 것입니다. 이 문제를 보여 주는 간단한 페이지 개인 설정 예제를 살펴보겠습니다.

     로그인해 주세요.

vs.

     수잔을 환영합니다!

간단해 보이지만 이 작은 웹 UI 비트조차도 페이지가 요청될 때마다 달라지는 몇 가지 정보가 필요합니다. 다음을 알아야 합니다.

  1. 사용자가 로그인되어 있나요?
  2. 사용자의 표시 이름은 무엇인가요?

일반적으로 페이지를 요청할 때마다 고유한 컨텍스트는 무엇인가요? 그리고 이 정보를 고려하기 위해 코드를 작성하려면 어떻게 해야 하나요?

실제로 HTTP의 상태 비 상태 비지정 특성으로 인해 웹 애플리케이션에서 추적해야 할 수 있는 다양한 컨텍스트 부분이 있습니다. 사용자가 웹 애플리케이션과 상호 작용하면 브라우저는 일련의 독립적인 HTTP 요청을 웹 서버로 보냅니다. 애플리케이션 자체는 이러한 요청을 사용자에게 만족스러운 환경으로 뜨개질하고 요청의 컨텍스트를 아는 것이 중요합니다.

ASP는 HTTP 요청의 컨텍스트를 추적하는 데 도움이 되는 요청애플리케이션 과 같은 몇 가지 내장 개체를 도입했습니다. ASP.NET 다음 단계를 수행하고 이러한 개체와 몇 가지 추가 컨텍스트 관련 개체를 Context라는 매우 편리한 내장 개체에 번들로 묶습니다.

ContextSystem.Web.HttpContext 형식의 개체입니다. ASP.NET Page 클래스의 속성으로 노출됩니다. 사용자 컨트롤 및 비즈니스 개체에서도 사용할 수 있습니다(나중에 자세히 설명). 다음은 HttpContext에서 롤업한 개체의 부분 목록입니다.

개체 설명
애플리케이션 애플리케이션의 모든 사용자가 액세스할 수 있는 값의 키/값 쌍 컬렉션입니다. 애플리케이션은 System.Web.HttpApplicationState 형식입니다.
ApplicationInstance 일부 요청 처리 이벤트를 노출하는 실제 실행 중인 애플리케이션입니다. 이러한 이벤트는 Global.asax 또는 HttpHandler 또는 HttpModule에서 처리됩니다.
Cache 캐시에 프로그래밍 방식으로 액세스할 수 있는 ASP.NET Cache 개체입니다. Rob Howard의 ASP.NET 캐싱 열 은 캐싱에 대한 좋은 소개를 제공합니다.
오류 페이지를 처리하는 동안 첫 번째 오류(있는 경우)가 발생했습니다. 자세한 내용은 Rob의 규칙 예외, 1부 를 참조하세요.
항목 단일 요청 처리에 참여하는 모든 구성 요소 간에 정보를 전달하는 데 사용할 수 있는 키-값 쌍 컬렉션입니다. 항목은 System.Collections.IDictionary 형식입니다.
요청 양식 또는 쿼리 문자열에 전달된 브라우저 정보, 쿠키 및 값을 포함하여 HTTP 요청에 대한 정보입니다. 요청은 System.Web.HttpRequest 형식입니다.
응답 HTTP 응답을 만들기 위한 설정 및 콘텐츠입니다. 요청은 System.Web.HttpResponse 형식입니다.
서버 Server는 Server.Execute(), Server.MapPath(), Server.HtmlEncode()를 비롯한 몇 가지 유용한 도우미 메서드가 있는 유틸리티 클래스입니다. 서버는 System.Web.HttpServerUtility 형식의 개체입니다.
세션 애플리케이션의 단일 사용자가 액세스할 수 있는 값의 키/값 쌍 컬렉션입니다. 애플리케이션은 System.Web.HttpSessionState 형식입니다.
추적 추적 기능에 대한 액세스를 제공하는 ASP.NET Trace 개체입니다. 자세한 내용은 Rob의 추적 문서를 참조하세요.
사용자 인증된 경우 현재 사용자의 보안 컨텍스트입니다. Context.User.Identity는 사용자의 이름입니다. User는 System.Security.Principal.IPrincipal 형식의 개체입니다.

ASP 개발자인 경우 위의 개체 중 일부는 매우 친숙해 보입니다. 몇 가지 향상된 기능이 있지만 대부분의 경우 ASP와 ASP.NET 동일하게 작동합니다.

컨텍스트 기본 사항

Context의 일부 개체도 Page에서 최상위 개체로 승격됩니다. 예를 들어 Page.Context.ResponsePage.Response 는 동일한 개체를 참조하므로 다음 코드는 동일합니다.

[Visual Basic® 웹 양식]

   Response.Write ("Hello ")
   Context.Response.Write ("There")

[C# 웹 양식]

   Response.Write ("Hello ");
   Context.Response.Write ("There");

비즈니스 개체의 Context 개체를 사용할 수도 있습니다. HttpContext.Current 는 현재 요청에 대한 컨텍스트를 편리하게 반환하는 정적 속성입니다. 이는 모든 종류의 방법으로 유용하지만 비즈니스 클래스의 캐시에서 항목을 검색하는 간단한 예는 다음과 같습니다.

[Visual Basic]

      ' get the request context
      Dim _context As HttpContext = HttpContext.Current

   ' get dataset from the cache
   Dim _data As DataSet = _context.Cache("MyDataSet")

[C#]

      // get the request context
      HttpContext _context = HttpContext.Current;

   // get dataset from cache
   DataSet _data = _context.Cache("MyDataSet");

작업의 컨텍스트

Context 개체는 "어떻게 해야 하나요?"라는 몇 가지 일반적인 ASP.NET 대한 답변을 제공합니다. 아마도이 보석이 얼마나 가치있는지 전달하는 가장 좋은 방법은 행동에 그것을 보여주는 것입니다. 다음은 내가 아는 최고의 컨텍스트 트릭 중 몇 가지입니다.

비즈니스 클래스에서 ASP.NET 추적 문을 내보내는 방법

대답: 쉬운! HttpContext.Current를 사용하여 Context 개체를 가져와서 Context.Trace.Write()를 호출합니다.

[Visual Basic]

Imports System
Imports System.Web

Namespace Context

   ' Demonstrates emitting an ASP.NET trace statement from a
   ' business class.

   Public Class TraceEmit
      
      Public Sub SomeMethod()
         
         ' get the request context
         Dim _context As HttpContext = HttpContext.Current
         
         ' use context to write the trace statement
         _context.Trace.Write("in TraceEmit.SomeMethod")

      End Sub

   End Class

End Namespace   

[C#]

using System;
using System.Web;

namespace Context
{
   // Demonstrates emitting an ASP.NET trace statement from a
   // business class.

   public class TraceEmit
   {

        public void SomeMethod() {
        
            // get the request context
            HttpContext _context = HttpContext.Current;

            // use context to write the trace statement
            _context.Trace.Write("in TraceEmit.SomeMethod");
        }
    }
}

내 비즈니스 클래스에서 세션 상태 값에 액세스하려면 어떻게 해야 하나요?

대답: 쉬운! HttpContext.Current를 사용하여 Context 개체를 가져와서 Context.Session에 액세스합니다.

[Visual Basic]

Imports System
Imports System.Web

Namespace Context

   ' Demonstrates accessing the ASP.NET Session intrinsic 
   ' from a business class.

   Public Class UseSession
   
      Public Sub SomeMethod()
         
         ' get the request context
         Dim _context As HttpContext = HttpContext.Current
         
         ' access the Session intrinsic
         Dim _value As Object = _context.Session("TheValue")

      End Sub

   End Class

End Namespace

[C#]

using System;
using System.Web;

namespace Context
{
   // Demonstrates accessing the ASP.NET Session intrinsic 
   // from a business class.

   public class UseSession
   {

        public void SomeMethod() {
        
            // get the request context
            HttpContext _context = HttpContext.Current;

            // access the Session intrinsic
            object _value = _context.Session["TheValue"];
        }
    }
}

대답: 애플리케이션의 BeginRequest 및 EndRequest 이벤트를 처리하고 Context.Response.Write를 사용하여 헤더 및 바닥글에 대한 HTML을 내보냅니다.

기술적으로 HttpModule 또는 Global.asax를 사용하여 BeginRequest 와 같은 애플리케이션 이벤트를 처리할 수 있습니다. HttpModules는 쓰기가 조금 더 어려우며 이 예제와 같이 단일 애플리케이션에서 사용되는 기능에 일반적으로 사용되지 않습니다. 따라서 애플리케이션 범위 Global.asax 파일을 대신 사용합니다.

ASP 페이지와 마찬가지로 ASP.NET 컨텍스트 내장 함수 중 일부는 Global.asax를 나타내는 클래스가 상속하는 HttpApplication 클래스의 속성으로 승격됩니다. Context 개체에 대한 참조를 가져오기 위해 HttpContext.Current를 사용할 필요가 없습니다. Global.asax에서 이미 사용할 수 있습니다.

이 예제에서는 및 태그와 <body> 머리글 섹션에 가로 규칙을 추가하고 또 다른 가로 규칙과 바닥글 섹션에 대한 끝 태그를 넣 <html> 습니다. 바닥글에는 저작권 메시지도 포함되어 있습니다. 결과는 아래 그림과 같습니다.

그림 1. 브라우저에서 렌더링된 표준 머리글 및 바닥글의 예

간단한 예제이지만 표준 헤더 및 탐색을 포함하거나 이에 대한 -- #include ---> 문을 출력<하도록 쉽게 확장할 수 있습니다. 한 가지 주의할 점은 머리글 또는 바닥글에 대화형 콘텐츠를 포함하려는 경우 ASP.NET 사용자 컨트롤을 대신 사용하는 것이 좋습니다.

[SomePage.aspx 원본 — 샘플 콘텐츠]

<FONT face="Arial" color="#cc66cc" size="5">
Normal Page Content
</FONT>

[Visual Basic Global.asax]

<%@ Application Language="VB" %>

<script runat="server">

      Sub Application_BeginRequest(sender As Object, e As EventArgs)

         ' emit page header
         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)

         ' emit page footer
         Context.Response.Write("<hr>" + ControlChars.Lf + _
      "Copyright 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) {

            // emit page header
            Context.Response.Write("<html>\n<body bgcolor=#efefef>\n<hr>\n");
        }

        void Application_EndRequest(Object sender, EventArgs e) {

            // emit page footer
            Context.Response.Write("<hr>\nCopyright 2002 Microsoft Corporation\n");
            Context.Response.Write("</body>\n</html>");
        }

</script>

사용자가 인증될 때 환영 메시지를 표시하려면 어떻게 해야 하나요?

답은 다음과 같습니다.사용자 컨텍스트 개체를 테스트하여 사용자가 인증되었는지 확인합니다. 그렇다면 User 개체에서도 사용자 이름을 가져옵니다. 물론 이 예제는 문서 시작 부분의 예제입니다.

[Visual Basic]

<script language="VB" runat="server">

    Sub Page_Load(sender As Object, e As EventArgs) {

        If User.Identity.IsAuthenticated Then
            welcome.Text = "Welcome " + User.Identity.Name
        Else
            ' not signed in yet, add a link to signin page
            welcome.Text = "please sign in!"
            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 = "Welcome " + User.Identity.Name;
        }
        else {
            // not signed in yet, add a link to signin page
            welcome.Text = "please sign in!";
            welcome.NavigateUrl = "signin.aspx";
        }
    }

</script>

<asp:HyperLink id="welcome" runat="server" maintainstate="false">
</asp:HyperLink>

그리고 지금 정말 멋진 무언가를 위해 : Context.Items

위의 예제에서는 약간의 컨텍스트 정보를 사용하여 웹 애플리케이션을 작성하는 것이 얼마나 쉬운지 보여 주길 바랍니다. 동일한 방식으로 애플리케이션에 고유한 일부 컨텍스트에 액세스할 수 있는 것이 좋지 않나요?

이것이 Context.Items 컬렉션의 목적입니다. 요청 처리에 참여하는 코드의 모든 부분에서 사용할 수 있는 방식으로 애플리케이션의 요청별 값을 보유합니다. 예를 들어 Global.asax, ASPX 페이지, 페이지 내의 사용자 컨트롤 및 페이지가 호출하는 비즈니스 논리에서 동일한 정보를 사용할 수 있습니다.

IBuySpy 포털 샘플 애플리케이션을 고려합니다. 단일 기본 페이지인 DesktopDefault.aspx를 사용하여 포털 콘텐츠를 표시합니다. 표시되는 콘텐츠는 선택된 탭과 인증된 경우 사용자의 역할에 따라 달라집니다.

그림 2. IbuySpy 홈페이지

querystring에는 요청되는 탭에 대한 TabIndex 및 TabId 매개 변수가 포함됩니다. 이 정보는 사용자에게 표시되는 데이터를 필터링하기 위해 요청을 처리하는 동안 사용됩니다. http://www.ibuyspy.com/portal/DesktopDefault.aspx?tabindex=1&tabid=2

querystring 값을 사용하려면 먼저 유효한 값인지 확인하고, 그렇지 않은 경우 약간의 오류 처리를 수행해야 합니다. 코드가 많지는 않지만 값을 사용하는 모든 페이지와 구성 요소에서 복제하시겠습니까? 물론 아닙니다! 포털 샘플에서는 TabId를 알고 나면 미리 로드할 수 있는 다른 정보가 있으므로 훨씬 더 많이 관련됩니다.

포털은 querystring 값을 매개 변수로 사용하여 새 "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
         
         ' Get TabIndex from querystring
         If Not (Request.Params("tabindex") Is Nothing) Then
            tabIndex = Int32.Parse(Request.Params("tabindex"))
         End If
         
         ' Get TabID from querystring
         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;

    // Get TabIndex from querystring

    if (Request.Params["tabindex"] != null) {               
        tabIndex = Int32.Parse(Request.Params["tabindex"]);
    }
                
    // Get TabID from querystring

    if (Request.Params["tabid"] != null) {              
        tabId = Int32.Parse(Request.Params["tabid"]);
    }

    Context.Items.Add("PortalSettings", 
new PortalSettings(tabIndex, tabId));
}

DesktopPortalBanner.ascx 사용자 컨트롤은 컨텍스트 에서 PortalSetting의 개체를 끌어와 포털의 이름 및 보안 설정에 액세스합니다. 실제로 이 모듈은 실제로 작동하는 컨텍스트 의 훌륭한 만능 예제입니다. 이 점을 설명하기 위해 코드를 약간 간소화하고 HTTP 또는 애플리케이션별 컨텍스트 가 굵게 액세스되는 모든 위치를 표시했습니다.

[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) {

        // Obtain PortalSettings from Current Context
  PortalSettings portalSettings = 
(PortalSettings) Context.Items["PortalSettings"];

        // Dynamically Populate the Portal Site Name
        siteName.Text = portalSettings.PortalName;

        // If user logged in, customize welcome message
        if (Request.IsAuthenticated == true) {
        
            WelcomeMessage.Text = "Welcome " + 
Context.User.Identity.Name + "! <" + 
"span class=Accent" + ">|<" + "/span" + ">";

            // if authentication mode is Cookie, provide a logoff link
            if (Context.User.Identity.AuthenticationType == "Forms") {
                LogoffLink = "<" + "span class=\"Accent\">|</span>\n" + 
"<a href=" + Request.ApplicationPath + 
"/Admin/Logoff.aspx class=SiteLink> Logoff" + 
"</a>";
            }
        }

        // Dynamically render portal tab strip
        if (ShowTabs == true) {

            tabIndex = portalSettings.ActiveTab.TabIndex;

            // Build list of tabs to be shown to user                                   
            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++;
            }          

            // Populate Tab List at Top of the Page with authorized 
// tabs
            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 Home</a>
<span class="Accent"> |</span> 
<a href="<%= Request.ApplicationPath %>/Docs/Docs.htm">
                Portal Documentation</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 포털에 대한 전체 원본을 찾아 실행하거나 다운로드하여 직접 실행할 수 있습니다.

요약

컨텍스트 는 "ASP.NET 더 나은 것"의 또 다른 기능 중 하나입니다. ASP의 이미 뛰어난 컨텍스트 지원을 확장하여 두 후크를 ASP.NET 새로운 런타임 기능에 추가합니다. 또한 Context.Items 를 매우 수명이 짧은 값에 대한 새 상태 메커니즘으로 추가합니다. 그러나 개발자가 얻을 수 있는 궁극적인 이점은 코드를 더 간결하고 쉽게 유지 관리할 수 있으며, 이것이 바로 우리 모두가 뒤처질 수 있는 컨텍스트입니다.