ASP.NET 서버 컨트롤을 사용하여 브라우저 기록 관리

Visual Studio 2010

업데이트: 2007년 11월

페이지 개발자는 ScriptManagerScriptManagerProxy 서버 컨트롤을 사용하여 브라우저 기록 항목을 관리하고 논리적 탐색 기능을 제공할 수 있습니다. ScriptManager 서버 컨트롤을 사용하면 응용 프로그램에 기록 지점을 설정할 수 있습니다. 두 컨트롤 모두 일부 기록 상태의 결과로 웹 페이지가 요청될 때 발생하는 탐색을 처리하는 데 사용할 수 있습니다.

기록 지점은 상태 정보를 통해 나타낼 수 있는 웹 응용 프로그램 내의 논리적 탐색 지점입니다. 상태 정보는 웹 응용 프로그램을 이전 상태로 복원하는 데 사용할 수 있습니다. 이러한 복원 작업은 상태 데이터를 직접 사용하거나 다른 곳에 저장된 상태 정보에 대한 식별자를 통해 수행할 수 있습니다.

기록 지점은 브라우저의 기록 스택에 URL로만 저장됩니다. 기록 상태는 쿼리 문자열의 데이터로 관리되거나 "#" 문자로 표시된 URL 부분 값으로 관리됩니다. URL에는 크기 제한이 있기 때문에 상태 정보는 가능한 한 작게 만들어야 합니다. 다음 예제에서는 상태를 식별하기에 충분한 기록 지점 데이터가 포함된 URL을 보여 줍니다. 이 기록 지점을 통해 응용 프로그램은 해당 상태의 페이지를 다시 만들 수 있습니다.

http://MySamples/Ajax/Default.aspx#&&state=2

사용자가 브라우저의 뒤로 단추를 클릭하면 이전에 방문한 URL로 이동되며, 이러한 URL에는 기록 지점 상태가 포함되어 있습니다. 웹 페이지의 클라이언트 코드는 URL에 기록 상태 데이터가 포함되어 있음을 감지하고 페이지에 대한 요청을 만들어 상태 정보를 전달합니다. 페이지는 요청을 처리할 때 기록 상태 정보를 읽고 비동기 포스트백을 트리거합니다. 그러면 ScriptManagerScriptManagerProxy 서버 컨트롤이 Navigate 이벤트를 발생시킵니다. 개발자는 이 이벤트를 처리하여 웹 응용 프로그램에 필요한 페이지를 다시 만들 수 있습니다.

Cc488548.alert_note(ko-kr,VS.100).gif참고:

이 항목에 나오는 예제 코드를 빌드하려면 Visual Studio 2008 서비스 팩 1 이상이 있어야 합니다.

다음 예제에서는 브라우저 기록을 사용하기 위한 ScriptManager 서버 컨트롤의 구문을 보여 줍니다.

<asp:ScriptManager runat="server" 
    EnableHistory="true|false"
    EnableStateHash="true|false"
    OnNavigate="navigateEventhandlerName">
</asp:ScriptManager>

기록 관리를 사용하려면 ScriptManager 서버 컨트롤을 통해 브라우저 기록 관리를 사용하도록 설정해야 합니다. 기록 기능은 기본적으로 지원되도록 설정되어 있지 않습니다. 기록 기능을 사용하도록 설정하면 브라우저마다 기록이 다르게 구현됩니다. Internet Explorer에서는 iframe 요소가 브라우저에 렌더링되는데 이로 인해 서버에 대해 추가 요청이 생성될 수 있습니다. 따라서 이 모델은 장단점을 고려하여 적합한 경우에 사용하는 것이 좋습니다. 다음 예제에서는 ScriptManager 컨트롤을 통해 기록 기능을 사용하도록 선언적으로 설정하는 방법을 보여 줍니다.

<asp:ScriptManager runat="server" ID="ScriptManager1" 
    EnableHistory="true" />

브라우저 기록 지점을 만들려면 ScriptManager 컨트롤의 AddHistoryPoint 메서드를 호출합니다. 이 메서드를 사용하면 서버 상태 및 키와 브라우저 내 기록 항목의 제목을 나타내는 선택적 데이터를 정의할 수 있습니다. 이러한 상태 데이터를 사용하여 이후 기록 탐색 이벤트가 발생할 때 해당 상태의 페이지를 다시 만들 수 있습니다. 기록 지점을 만들면 serialize되고 암호화된 데이터가 웹 페이지의 URL에 기본적으로 추가됩니다. 이렇게 만들어진 URL은 브라우저의 기록 스택에 포함됩니다.

Cc488548.alert_note(ko-kr,VS.100).gif참고:

기록 지점은 선택 영역 클릭과 같은 사용자 작업에 대한 응답으로만 추가해야 합니다. 응용 프로그램 코드 실행의 결과로는 일반적으로 기록 지점을 추가하지 않습니다.

다음 예제에서는 UpdatePanel 컨트롤을 사용하여 페이지에 대해 비동기 포스트백을 설정합니다. 또한 비동기 포스트백을 트리거하는 단추의 Click 이벤트 처리기에서 ScriptManager 컨트롤을 사용하여 기록 지점을 추가합니다. 이 경우 브라우저의 뒤로 단추를 클릭하면 웹 페이지에서 나가는 대신 페이지의 이전 기록 상태가 탐색됩니다.

이 기능에 대한 예제 실행.

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Globalization" %>
<%@ Import Namespace="System.Collections.Generic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static String key = "s";

    // Handle the Navigate event
    public void OnNavigateHistory(object sender, HistoryEventArgs e) {
        LabelHistoryData.Text = Server.HtmlEncode(e.State[key]);
    }

    // On button click, handle the event and set a history point. 
    public void ButtonClick(object sender, EventArgs e) {
        LabelHistoryData.Text = ((Button)sender).Text;
        ScriptManager.GetCurrent(this).AddHistoryPoint(key, LabelHistoryData.Text);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5 Extensions: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Microsoft ASP.NET 3.5 Extensions: Adding Server-side Browser History Points</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code>event.<br />
                    </li>
                </ol>
            </div>

            <p>
                In this example, three buttons outside the <code>UpdatePanel</code> control can
                trigger an asynchronous postback. You can see the results of the update through
                the date and time that renders in the Web page along with a value indicating the 
                button that triggered the partial refresh.</p>
            <p>
                When you press a button, the server-side <code>Click</code> event handler for the button 
                stores data and uses the data as a history point. If you click the browser's Back button, 
                you will see the state Web page return to a previous button value, representing the previous 
                button you pressed. However, you will see that the time within the Web page continues to be 
                incremented.
            </p>
            <p>To see the effect of this logic, do the following.</p>

            <ol>
                <li>Press <b>1</b>. See the panel refresh.</li>
                <li>Press <b>3</b>. See the panel refresh.</li>
                <li>Press <b>2</b>. See the panel refresh.</li>
                <li>Press the browser's Back button. Note that the panel is refreshed with previous
                    data, but the date and time are updated to current values.</li>
            </ol>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button3" EventName="Click" />
                </Triggers>
                <ContentTemplate>
                    <asp:Panel runat="server" CssClass="box" ID="Content" Height="40px">
                        Date and Time:
                        <%= DateTime.Now.ToLongTimeString() %>
                        <br />
                        Page's refresh state:
                        <asp:Label runat="server" ID="LabelHistoryData" />
                    </asp:Panel>
                </ContentTemplate>
            </asp:UpdatePanel>
            <p />
            <asp:Button runat="server" ID="Button1" Text="Key 1" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button2" Text="Key 2" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button3" Text="Key 3" OnClick="ButtonClick" />
        </div>
    </form>
</body>
</html>


요청에서 서버 상태가 검색되면 Navigate 이벤트가 발생합니다. 이는 서버에 대한 비동기 포스트백과 유사합니다. 포스트백이 탐색을 위해 발생했는지 아니면 다른 목적으로 발생했는지 확인해야 하는 경우 IsNavigating 속성을 확인하면 됩니다. 이 속성이 true로 설정되어 있으면 포스트백이 탐색 호출로 시작된 것입니다.

다음 예제에서는 UpdatePanel 컨트롤 내의 Wizard 서버 컨트롤을 보여 줍니다. 이 경우 사용자가 마법사를 탐색할 때 Wizard 컨트롤이 비동기 포스트백을 수행합니다. 예제에서는 마법사 단계가 탐색될 때 코드가 기록 지점을 추가합니다.

이 기능에 대한 예제 실행.

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static readonly string stepKey = "s";

    protected void OnNavigateHistory(object sender, HistoryEventArgs args)
    {
        string stateString = args.State[stepKey];
        int step = (stateString != null) ? int.Parse(stateString) : 0;
        MachineConfiguratorWizard.ActiveStepIndex = step;
    }

    protected void OnActiveStepChanged(object sender, EventArgs e)
    {
        if (!ScriptManager1.IsNavigating && IsPostBack) {
            int index = MachineConfiguratorWizard.ActiveStepIndex;
            ScriptManager1.AddHistoryPoint(stepKey, index.ToString(), "Step " + (index+1).ToString());
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5 Extensions: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <div>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Microsoft ASP.NET 3.5 Extensions: Adding Server-side Browser History Points</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code> event.
                    </li>
                    <li>Protecting the history code with <code>IsNavigating</code>
                    </li>
                </ol>
            </div>
        <p>
        In this example, the <code>Wizard</code> server control provides it's own navigation, but 
        as each step is selected a history point is added. In order to do this, a history point is only added if the page is not being refreshed beacuse of a history point.</p>
            <asp:UpdatePanel runat="server" ID="WizardPanel">
                <ContentTemplate>
                    <asp:Wizard ID="MachineConfiguratorWizard" runat="server" ActiveStepIndex="0" BackColor="#dddddd"
                        BorderWidth="10" CellPadding="10" CellSpacing="10" Height="200px" Width="700px"
                        FinishPreviousButtonText="<" StartNextButtonText=">" StepNextButtonText=">" StepPreviousButtonText="<"
                        FinishCompleteButtonText="<|>" OnActiveStepChanged="OnActiveStepChanged">
                        <WizardSteps>
                            <asp:WizardStep ID="Step1" runat="server" Title="Step 1">
                                <h2>
                                    STEP 1</h2>
                                <br />
                            </asp:WizardStep>
                            <asp:WizardStep ID="Step2" runat="server" Title="Step 2">
                                <h2>
                                    STEP 2</h2>
                                <br />
                            </asp:WizardStep>
                            <asp:WizardStep ID="Step3" runat="server" Title="Step 3">
                                <h2>
                                    STEP 3</h2>
                                <br />
                            </asp:WizardStep>
                        </WizardSteps>
                        <StepStyle Font-Names="tahoma" Font-Size="Smaller" VerticalAlign="Top" />
                        <SideBarStyle Font-Size="Small" VerticalAlign="Top" BackColor="#FFFFC0" Font-Names="tahoma" />
                        <FinishPreviousButtonStyle BackColor="White" BorderColor="Black" BorderWidth="3px"
                            Font-Names="Tahoma" Font-Size="Medium" />
                        <NavigationButtonStyle BackColor="White" BorderColor="Black" BorderStyle="Solid"
                            BorderWidth="3px" Font-Names="Tahoma" Font-Size="Medium" />
                        <FinishCompleteButtonStyle Font-Names="Tahoma" Font-Size="Medium" />
                    </asp:Wizard>
                </ContentTemplate>
            </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>


기록 지점을 만드는 경우 해당 기록 지점 상태에 대해 저장할 정보를 결정해야 합니다. 적어도 응용 프로그램이 상태를 식별하는 데 필요한 키/값 쌍은 반드시 사용해야 합니다. 이외 다른 키/값 쌍을 저장할 수도 있습니다. 그러나 이 정보는 URL에 저장되고 URL에는 브라우저에 지정된 대로 크기가 제한되므로 해당 상태를 다시 만드는 데 필요한 정보만 저장해야 합니다.

서버 상태 정보는 ScriptManager 서버 컨트롤의 EnableHistory 속성에 정의한 설정에 따라 해시되거나 해시되지 않은 형식으로 URL에 저장됩니다.

URL에서 기록 상태는 # 문자(단편 구분 기호)로 구분됩니다. 다음 예제와 같이 상태 정보 앞에는 "&&" 구분 기호가 옵니다.

Default.aspx#&&s=Key+2

EnableHistorytrue로 설정하면 기록 상태 단편이 먼저 암호화된 후 웹 페이지 URL에 추가됩니다. 이렇게 하면 공격자가 상태 데이터를 변경하기가 어려워지므로 보안이 향상됩니다. 그러나 정보가 암호화되더라도 중요한 데이터는 상태 필드에 저장하지 않아야 합니다.

상태 정보를 해시할 경우 길이가 길고 사용자에게 의미 없는 정보가 포함된 URL이 생성됩니다.

브라우저 기록 스택의 항목은 대개 해당 항목의 페이지 제목으로 식별됩니다. 예를 들어 브라우저의 기록 목록을 보면 최근에 방문한 페이지의 제목을 볼 수 있습니다. URL 주소 상자의 드롭다운 목록을 사용하면 이 목록을 볼 수 있습니다.

응용 프로그램에서 기록 지점 항목을 만들면 해당 항목을 식별하는 데 기본적으로 페이지 제목이 사용됩니다. 같은 페이지에서 여러 기록 지점을 추가하는 경우 기본적으로 모든 항목의 제목이 같습니다.

그러나 개별 기록 항목에 의미 있는 제목을 지정할 수 있습니다. 서버 코드에서 AddHistoryPoint 메서드를 호출하여 기록 지점을 만들면 제목 정보를 포함할 수 있습니다.

다음 예제는 제목 항목을 포함하여 앞의 예제를 약간 변형한 것입니다.

이 기능에 대한 예제 실행.

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Globalization" %>
<%@ Import Namespace="System.Collections.Generic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static String key = "s";

    // Handle the Navigate event
    public void OnNavigateHistory(object sender, HistoryEventArgs e) {
        LabelHistoryData.Text = Server.HtmlEncode(e.State[key]);
    }

    // On button click, handle the event and set a history point. 
    public void ButtonClick(object sender, EventArgs e) {
        LabelHistoryData.Text = ((Button)sender).Text;
        ScriptManager.GetCurrent(this).AddHistoryPoint(key, LabelHistoryData.Text, "Entry: " + LabelHistoryData.Text);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5 Extensions: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Microsoft ASP.NET 3.5 Extensions: Adding Server-side Browser History Points and Title Entries</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point and add titles.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code>event.<br />
                    </li>
                </ol>
            </div>

            <p>
                In this example, three buttons outside the <code>UpdatePanel</code> control can
                trigger an asynchronous postback. You can see the results of the update through
                the date and time that renders in the Web page along with a value indicating the 
                button that triggered the partial refresh.</p>
            <p>
                When you press a button, the server-side <code>Click</code> event handler for the button 
                stores data and uses the data as a history point. If you click the browser's Back button, 
                you will see the state Web page return to a previous button value, representing the previous 
                button you pressed. However, you will see that the time within the Web page continues to be 
                incremented.
            </p>
            <p>To see the effect of this logic, do the following.</p>

            <ol>
                <li>Press <b>1</b>. See the panel refresh.</li>
                <li>Press <b>3</b>. See the panel refresh.</li>
                <li>Press <b>2</b>. See the panel refresh.</li>
                <li>Press the browser's Back button. Note that the panel is refreshed with previous
                    data, but the date and time are updated to current values.</li>
                <li>Press the browser's "Recent Pages" drop down menu and review the history entries and their titles.</li>
            </ol>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button3" EventName="Click" />
                </Triggers>
                <ContentTemplate>
                    <asp:Panel runat="server" CssClass="box" ID="Content" Height="40px">
                        Date and Time:
                        <%= DateTime.Now.ToLongTimeString() %>
                        <br />
                        Page's refresh state:
                        <asp:Label runat="server" ID="LabelHistoryData" />
                    </asp:Panel>
                </ContentTemplate>
            </asp:UpdatePanel>
            <p />
            <asp:Button runat="server" ID="Button1" Text="Key 1" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button2" Text="Key 2" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button3" Text="Key 3" OnClick="ButtonClick" />
        </div>
    </form>
</body>
</html>


표시: