Standardní způsob interakce mezi uživatelem a serverem se v ASP.NET zajišťuje pomocí takzvaného Postbacku, kdy se na server odešle obsah celého formuláře, aplikace informace roztřídí a odešle upravenou stránku zpět klientovi. Tento model má své velké nevýhody. Především jde o to, že klient si musí stáhnout opět celou stránku. V mnoha případech se posílá informací zbytečně mnoho, respektive stačí překreslit jenom zlomek stránky. V případě Postbacku se však celý zbytek stránky musí s těmito informacemi „svézt“, ať už jsou důležité pro danou operaci nebo ne. V těchto případech by měl do hry vstoupit Callback.

Přínos klientských Callbacků

Když ASP.NET 1.0 přinesl do světa programování webových aplikací Postback, všichni jsme byli nadšeni z toho, jak jednoduše se dá pracovat s prvky na stránce. Postback má však velmi mnoho nevýhod. Pokud vyvoláte Postback, odešle se celá stránka na server, tam se zpracují informace, aplikace se podle nich nějak zařídí, ASP.NET opět vytvoří všechny prvky a pošle opět celou stránku zpět klientovi.

V mnoha případech je úplně zbytečné odesílat například kvůli jednomu textovému poli na server celou stránku. Uživatele to poměrně dost zdržuje a server zatěžuje. Vezmeme si malý příklad…

Máte za úkol napsat formulář, který slouží k registrování uživatelů na vašem webovém sídle. Na formuláři bude nejspíš textové pole, kam uživatel vyplní svoje přihlašovací jméno, které musí být logicky unikátní, dále pak jméno, příjmení, heslo a další položky. V klasickém modelu formulář funguje tak, že uživatel vyplní všechny informace a klepne na tlačítko „odeslat“. Aplikace vše přijme, ale jako první se zabývá pouze unikátností uživatelského jména. Pokud je již jméno použito, pošle vše zpět na klienta s hlášením, že uživatelské jméno je již zabrané, ať si zvolí jiné. Je-li to opravdu velký portál, není neobvyklé, že se tento scénář opakuje třeba pětkrát nebo i vícekrát.

Proti zbytečnému odesílání formulářů na server nám v mnoha případech pomohou validátory, které zjistí už na klientovi, je-li hodnota platná či nikoli. Například formát emailu, „síla“ hesla a podobně. V tomto případě je však zřejmé, že validátor si jen tak na klientovi neporadí. Těžko budeme validátor učit všechna již použitá uživatelská jména. Musíme tedy najít způsob, jak si pomoci jinak.

Hodilo by se, kdyby formulář mohl ihned po vyplnění uživatelského jména v tichosti odeslat na server pouze ono vyplněné uživatelské jméno a zatímco uživatel vyplňuje další informace, server by ověřil, jestli je jméno volné. Klientovi by pak poslal jen samotnou informaci o tom, je-li jméno použitelné. Tento model je na první pohled mnohonásobně efektivnější, než odesílání celé stránky tam a zpět. Toto je typický model pro klientský Callback.

Jak klientský Callback funguje

Ačkoli není tento odstaveček pro většinu programátorů příliš důležitý, zde bych se měl alespoň ve zkratce zmínit o tom, jak klientský Callback zajišťuje komunikaci mezi klientem a serverem.

Klientský formulář inicializuje Callback voláním funkce generované metodou GetCallbackEventReference. Na to Callback manager vytvoří asynchronní XML-HTTP volání na server. Na stránce se zavolá metoda RaiseCallbackEvent() a GetCallbackResult(), která vrátí zpět na klienta informace, které požaduje.

Zdá se to docela jednoduché a ve skutečnosti to i jednoduché je. Stránce stačí implementovat rozhraní ICallBackEventHandler a implementovat metody, které toto rozhraní definuje – zmiňované RaiseCallbackEvent() a GetCallbackResult().

Klientský Callback v praxi

Myslím, že jsem dostatečně nastínil, jak Callback funguje. Nyní bychom si mohli ukázat příklad s registrací uživatelů s využitím tohoto systému na reálné aplikaci. Uvažujme například takovouto stránku:

<head runat=“server“>
 <title>Callback</title>
 <script type=“text/javascript“>
  function CheckUserName(result)
  {
   document.getElementById(‚<%= lblStatusId %>‘).innerText = result;
  }
 </script>
</head>
<body>
 <form id=“form1″ runat=“server“>
 <table>
  <tr>
   <td>
    Uživatelské jméno:</td>
   <td>
    <asp:TextBox ID=“tbUserName“ runat=“server“ / >&nbsp;
    <asp:Label ID=“lblStatus“ runat=“server“ />
   </td>
  </tr>
  <tr>
   <td>
    Jméno:</td>
   <td>
    <asp:TextBox ID=“tbFN“ runat=“server“ />
   </td>
  </tr>
  <tr>
   <td>
    Příjmení:</td>
   <td>
    <asp:TextBox ID=“tbLN“ runat=“server“ />
   </td>
  </tr>
  <tr>
   <td colspan=“2″ align=“center“>
    <asp:Button ID=“btnSubmit“ runat=“server“ Text=“Odeslat“ />
   </td>
  </tr>
 </table>
 </form>
</body>

Abychom se měli od čeho odpíchnout ve výkladu, začneme právě tímto formulářem. Je to sice tak trošku „odzadu“, ale myslím si, že je to nejlepší cesta. Na této stránce je pro nás zajímavý pouze JavaScript v hlavičce:

function CheckUserName(result)
{
 document.getElementById(‚<%= lblStatusId %>‘).innerText = result;
}

Je to funkce, kterou později zaregistrujeme v kódu a která slouží k přijetí výsledku zpětného volání. Dalšími prvky na stránce jsou již jen textová pole a tlačítko. Prozatím jsme nedefinovali žádnou obsluhu zpětného volání u těchto prvků, to provedeme až v kódu – zaregistrujeme Callback pro textové pole tbUserName. Budeme chtít, aby se aplikace po změně textu v poli (JavaScriptová událost onchange) ujistila, že je zvolené uživatelské jméno ještě k dispozici. Přejděme tedy k rozboru souboru Default.aspx.cs:

public partial class _Default : System.Web.UI.Page, System.Web.UI.ICallbackEventHandler
{
 public string lblStatusId = String.Empty;
 private string _callbackArg;
 
 public void RaiseCallbackEvent(string eventArgument)
 {
  _callbackArg = eventArgument;
 }
 
 public string GetCallbackResult()
 {
  // zde by se melo spojit s db a zjistit,
  // je-li volne nebo ne
  return _callbackArg + “ je již použito!“;
 }
 
 protected void Page_Init(object sender, EventArgs e)
 {
  lblStatusId = lblStatus.ClientID;
 
  string argument = „document.getElementById(‚“ + tbUserName.ClientID + „‚).value“;
 
  string clientCallback = „CheckUserName“;
 
  string CallbackRef = Page.ClientScript.GetCallbackEventReference(this, argument, clientCallback, „null“);
 
  tbUserName.Attributes[„onchange“] = String.Format(„javascript:{0}“, CallbackRef);
 }
}

Tento příklad je přesně v duchu výše nastíněného modelu. Třída _Default implementuje rozhraní ICallbackEventHandler a definuje obě metody, které toto rozhraní předepisuje:

void RaiseCallbackEvent (string eventArgument)
string GetCallbackResult()

První metoda, tedy RaiseCallbackEvent, se zavolá nejdříve a přejme jako argument textovou proměnnou, obvykle pojmenovoanou eventArgument. Tímto argumentem se aplikace dozví, co uživatel na formuláři vyplnil.

Metoda GetCallbackResult() figuruje jako prostředek pro vrácení informací zpět na klienta. V našem příkladě se aplikace nijak nerozhoduje a rovnou vrací text značící, že uživatelské jméno je již použito. Pro demonstraci to plně postačuje, v reálné aplikaci by se samozřejmě muselo zjistit, jak na tom vybrané uživatelské jméno je a poté vrátit nějaký výsledek.

Dohodu s rozhraním ICallbackEventHandler jsme již naplnili, už stačí jen zaregistrovat zpětné volání na klientu, aby věděl co a kdy na server odesílat. To provádíme v Page_Init.

Nejdříve by bylo dobré říci klientskému skriptu, do kterého objektu má výsledek promítnout. Na úrovni třídy jsem deklaroval veřejnou řetězcovou proměnou, do které v Page_Init uložím id objektu. Tuto hodnotu využívám v klientském skriptu, který jsem uváděl výše:

document.getElementById(‚<%= lblStatusId %>‘).innerText = result;

Dále musíme zjistit argument zpětného volání. Bude jím hodnota z prvku tbUserName. Tuto hodnotu je nutné získat pomocí JavaScriptu na klientovi, není možné se spoléhat na ASP.NET.

Další proměnná, která se v kódu vyskytuje, reprezentuje název klientské funkce, která přijme výsledek zpětného volání. Je to název JavaScriptové funkce, kterou jsme definovali v souboru Default.aspx.

Poslední proměnou použijeme jako kontejner pro název funkce zpětného volání, kterou vytváří ASP.NET. Pro získání tohoto názvu voláme metodu Page.ClientScript.GetCallbackEventReference(), které předáme předem připravené parametry. První argument specifikuje objekt, který zpětné volání obsluhuje, dále pak argument volání a název klientské funkce.

Souhrn

Klientský Callback umožňuje zpříjemnit uživatelům pobyt na vašem webu a odlehčit práci serveru, který nemusí pořád dokola obsluhovat vykreslování stránky. Náš příklad byl pouze modelový a v praxi půjde v mnoha případech o více hodnot než jen text z textového pole. Práce s více hodnotami se samozřejmě obsloužit dá, je však na programátorovi, jak se vypořádá s více hodnotami v jedné textové proměnné. Nabízí se použít nějaký znak jako oddělovač hodnot.

Mohli bychom třeba náš příklad modifikovat takovým způsobem, že by přihlašovací jméno generoval ze jména, příjmení a pořadového čísla, jak se tomu standardně děje v některých organizacích. Aby toto mohl zpracovat klientský Callback, museli bychom na server odeslat dvě hodnoty – jméno a příjmení. Server by pak odpověděl jedním stringem s přihlašovacím jménem. V tomto případě bychom mohli postupovat tak, že bychom jako argument použili jméno a příjmení oddělené nějakým znakem, například zavináčem. Na serveru potom přijatý argument pomocí metody String.Split() rozdělíme opět na dvě hodnoty, ty použijeme pro generování uživatelského jména a vrátíme zpět na klienta.

Způsobů je však více a záleží na vkusu a schopnostech programátora.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

1 Příspěvěk v diskuzi

  1. Hezký den a prosbu…hraji ráda hry, však čistě náhodou a nevím jak se to stalo, mne nejde upravi AVATER..píše mi to:
    An error occured: Objekt console není definován.
    An error occured: Objekt neumožňuje použití vlastnosti či metody _callback.
    Nevím zda je to náhoda a nebo něčí práce. Dříve mi to šlo dobře.
    Děkuji za pomoc, čí vysvětlení proč to tak je

Odpovědět