유효성 검사 기능이 있는 클라이언트 콜백 구현 예제

Visual Studio 2010

업데이트: 2007년 11월

클라이언트 콜백에서 클라이언트 스크립트 함수는 ASP.NET 웹 페이지에 요청을 보내고, 이 웹 페이지에서는 콜백을 처리하기 위해 약식 버전의 일반 수명 주기를 실행합니다. 올바른 UI(사용자 인터페이스)를 사용하여 콜백 이벤트가 시작되었는지 확인하기 위해 콜백의 유효성을 검사할 수 있습니다. 콜백 유효성 검사는 웹 페이지를 렌더링하는 동안 유효성 검사를 위한 이벤트를 등록한 다음 콜백하는 동안 해당 이벤트의 유효성을 검사하는 과정으로 진행됩니다.

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

이벤트 유효성을 검사하면 위조된 다시 게시로부터 웹 응용 프로그램을 보호하는 데 도움이 되지만 재생 공격을 막을 수는 없습니다. 더 포괄적인 이벤트 유효성 검사 체계에서는 웹 응용 프로그램의 특징과 해당 리소스에 액세스하는 사용자의 권한을 고려해야 합니다. 자세한 내용은 ASP.NET 웹 응용 프로그램 보안을 참조하십시오.

여기 나와 있는 예제는 클라이언트 콜백 구현 예제(C#)클라이언트 콜백 구현 예제(Visual Basic)를 확장한 것입니다. 이들 예제에서 ListBox1이라는 ListBox 컨트롤은 제품 목록을 표시하는 서버측 컨트롤입니다. 제품 재고 정보를 가져오는 콜백은 Button 서버 컨트롤이 아니라 HTML <button> 요소에서 수행합니다. 여기에서는 제품을 판매 중인지에 대한 정보를 추가로 제공하고 인증된 사용자만 이 정보를 볼 수 있도록 예제를 확장했습니다. 추가 콘텐츠를 표시하도록 설정된 LoggedInTemplate 속성과 함께 LoginView 컨트롤을 사용합니다. 웹 페이지를 사용하는 사람이면 누구든지 재고 정보를 가져오는 콜백을 실행할 수 있지만, 로그인한 사용자는 판매 정보를 가져오는 콜백도 실행할 수 있습니다. 판매 정보에 대한 콜백은 사용자가 인증된 경우에만 이벤트 유효성 검사에 대해 등록됩니다. 따라서 인증되지 않은 사용자는 이 콜백을 실행할 수 없습니다.

설명

다음 예제의 웹 페이지는 재고가 남아 있는 항목의 수를 확인하고 항목이 판매되고 있는지 여부를 확인하는 데이터베이스 조회를 에뮬레이트합니다. 예제를 쉽게 이해할 수 있도록 두 개의 사전 목록이 데이터 저장소로 사용됩니다. 프로덕션 응용 프로그램에서는 데이터베이스가 대신 사용됩니다. 이 예제에서는 인증된 사용자만을 대상으로 하는 콜백을 익명 사용자가 실행하지 못하도록 클라이언트 콜백을 유효성 검사하는 시나리오를 보여 줍니다.

코드

<%@ Page Language="C#" AutoEventWireup="true" 
  CodeFile="ClientCallback.aspx.cs" Inherits="ClientCallback" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 
  1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>ASP.NET Example</title>
<script type="text/javascript">    
    function ReceiveServerData(rValue)
    {
        Results.innerText = rValue;
    }
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <div>
      <asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
      <br />
      <br />
      <button id="LookUpStockButton" onclick="LookUpStock()">Look Up Stock</button>
      <asp:LoginView id="LoginView1" runat="server">
      <LoggedInTemplate>
         <button id="LookUpSaleButton" onclick="LookUpSale()">Look Up Back Order</button>
      </LoggedInTemplate>
      </asp:LoginView>
      <br />
      Item status: <span id="Results"></span>
    </div>
  </form>
</body>
</html>


using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class ClientCallback : System.Web.UI.Page,
     System.Web.UI.ICallbackEventHandler
{
    protected System.Collections.Specialized.ListDictionary catalog;
    protected System.Collections.Specialized.ListDictionary saleitem;
    protected String returnValue;
    protected String validationLookUpStock = "LookUpStock";
    protected String validationLookUpSale = "LookUpSale";
    protected void Page_Load(object sender, EventArgs e)
    {
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
            validationLookUpStock, "function LookUpStock() {  " +
            "var lb = document.forms[0].ListBox1; " +
            "var product = lb.options[lb.selectedIndex].text;  " +
            @"CallServer(product, ""LookUpStock"");}  ", true);
        if (User.Identity.IsAuthenticated)
        {
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
            validationLookUpSale, "function LookUpSale() {  " +
            "var lb = document.forms[0].ListBox1; " +
            "var product = lb.options[lb.selectedIndex].text;  " +
            @"CallServer(product, ""LookUpSale"");} ", true);
        }

        String cbReference = "var param = arg + '|' + context;" + 
            Page.ClientScript.GetCallbackEventReference(this,
            "param", "ReceiveServerData", "context");
        String callbackScript;
        callbackScript = "function CallServer(arg, context)" +
            "{ " + cbReference + "} ;";
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
            "CallServer", callbackScript, true);

        catalog = new System.Collections.Specialized.ListDictionary();
        saleitem = new System.Collections.Specialized.ListDictionary();
        catalog.Add("monitor", 12);
        catalog.Add("laptop", 10);
        catalog.Add("keyboard", 23);
        catalog.Add("mouse", 17);
        saleitem.Add("monitor", 1);
        saleitem.Add("laptop", 0);
        saleitem.Add("keyboard", 0);
        saleitem.Add("mouse", 1);

        ListBox1.DataSource = catalog;
        ListBox1.DataTextField = "key";
        ListBox1.DataBind();
    }
    public void RaiseCallbackEvent(String eventArgument)
    {
        string[] argParts = eventArgument.Split('|');
        if ((argParts == null) || (argParts.Length != 2))
        {
            returnValue = "A problem occurred trying to retrieve stock count.";
            return;
        }
        string product = argParts[0];
        string validationaction = argParts[1];
        switch (validationaction)
        {
            case "LookUpStock":
                try
                {
                    Page.ClientScript.ValidateEvent("LookUpStockButton", validationaction);
                    if (catalog[product] == null)
                    {
                        returnValue = "Item not found.";
                    }
                    else
                    {
                        returnValue = catalog[product].ToString() + " in stock.";
                    }
                }
                catch
                {
                    returnValue = "Can not retrieve stock count.";
                } 
                break;
            case "LookUpSale":
                try
                {
                    Page.ClientScript.ValidateEvent("LookUpSaleButton", validationaction);
                    if (saleitem[product] == null)
                    {
                        returnValue = "Item not found.";
                    }
                    else
                    {
                        if (Convert.ToBoolean(saleitem[product]))
                            returnValue = "Item is on sale.";
                        else
                            returnValue = "Item is not on sale.";
                    }
                }
                catch
                {
                    returnValue = "Can not retrieve sale status.";
                }
                break;
        }

    }
    public String GetCallbackResult()
    {
        return returnValue;
    }
    protected override void Render(HtmlTextWriter writer)
    {
        Page.ClientScript.RegisterForEventValidation("LookUpStockButton",
            validationLookUpStock);
        if (User.Identity.IsAuthenticated)
        {
            Page.ClientScript.RegisterForEventValidation("LookUpSaleButton",
                validationLookUpSale);
        }
        base.Render(writer);
    }
}


설명

이 웹 페이지에서는 모니터와 키보드 등 일련의 제품에 대해 현재 재고가 있는 항목의 수를 확인하기 위해 데이터베이스 조회를 에뮬레이트합니다. 코드 예제를 간소화하기 위해 소규모의 항목 집합이 포함된 사전 목록이 데이터베이스로 사용됩니다. 테이블의 각 항목에 대해 키는 항목 이름(예: 모니터)이고 값은 재고가 있는 항목의 수입니다. 프로덕션 응용 프로그램에서는 데이터베이스가 대신 사용됩니다.

페이지가 실행되면 ListBox 컨트롤에 제품 목록이 표시되도록 ListBox 컨트롤이 해시 테이블에 바인딩됩니다. 인증된 사용자의 경우 이 페이지는 해당 onclick 이벤트가 LookUpStock이라는 클라이언트 함수와 LookUpSale이라는 클라이언트 함수에 각각 바인딩된 두 개의 HTML <button> 요소로 렌더링됩니다. 익명 사용자의 경우 이 페이지는 해당 onclick 이벤트가 LookUpStock에 바인딩된 한 개의 HTML <button> 요소로 렌더링됩니다. LoginView 컨트롤은 어떤 단추를 표시할지 지정하는 데 사용됩니다. 페이지의 재정의된 Render 이벤트에서 단추가 유효성 검사를 위해 등록됩니다. 사용자가 인증되지 않은 경우에는 LookUpSale에 대한 콜백을 시작하는 단추가 등록되지 않고 콜백을 시도하더라도 실패합니다.

코드 숨김 페이지는 RegisterClientScriptBlock 메서드를 통해 클라이언트측 스크립트를 페이지에 추가합니다. 페이지에 추가되는 스크립트에는 CallServer라는 함수가 포함되어 있으며 이 함수는 GetCallbackEventReference 메서드에서 서버에 다시 게시할 메서드의 이름을 가져옵니다.

클라이언트 콜백은 전달된 제품의 재고를 확인하는 RaiseCallbackEvent 메서드를 호출합니다. GetCallbackResult 메서드는 값을 반환합니다. 이때 클라이언트 스크립트와 서버 코드 간에 전달되는 인수는 문자열만 가능합니다. 여러 값을 전달하거나 받으려면 입력 문자열이나 반환 문자열에 각각 값을 연결합니다.

ms366515.alert_security(ko-kr,VS.100).gif보안 정보:

웹 페이지와 클라이언트 콜백에서 데이터를 삽입, 업데이트 또는 삭제하는 작업이나 중요한 데이터 표시를 처리하는 경우 콜백의 유효성을 검사하여 올바른 사용자 인터페이스 요소를 통해 콜백이 실행되는지 확인하는 것이 좋습니다.

표시: