ugrás a tartalomhoz

AJAX fejlesztés - kommunikáció

Bártházi András · 2006. Aug. 22. (K), 06.45
Folytatva korábbi bejegyzéseimet, az alapok és a megjelenítés után szeretnék egy kicsit írni a kommunikációról is a webalkalmazás és a szerver között. Egy "igazi" webalkalmazásnál (ahol egy összetett felületünk van, s a lényeg a böngészőnkben fut, nem pedig a szerveren) alapvetően két formátum merülhet fel: az egyik a JSON, a másik pedig az XML - de a többi lehetőségre is kitérek röviden.

A dolog egyébként úgy tűnik, hogy nem csak engem foglalkoztat, hiszen pont egy hasonló írást láthattunk Szalai Ferenc tollából is, én is hasonlókat fogok írni, mint ő.

Lássuk az alapokat: ha a kliens (böngésző) és a szerver (szerver oldali program) kommunikál egymással, mindenképpen kell választanunk valamilyen protokollt, és valamilyen adatformátumot. A protokoll kiválasztása esetünkben egyértelmű, mivel nem nagyon támogat mást a JavaScript és maga a böngésző: ez pedig a HTTP. A kérdés valójában csak az, hogy ezen milyen adatokat küldjünk a kliens felé.

A következő adatformátumok jöhetnek szóba:
  • JSON
  • XML
  • HTML
  • JavaScript kód

Mindegyik formátum másra lehet jó, máskor használható, az alkalmazás típusától nagyban függhet, hogy melyik az ideális. Arról, hogy milyen alkalmzástípusok vannak, talán majd még születik egy bejegyzésem, röviden: különválaszthatóak azok, ahol inkább a szerver oldalon, s azok, ahol inkább a kliens oldalon van a logika. A hangsúly persze az inkább szón van, lehet a működés teljesen vegyes is (bár nem feltétlenül célszerű keverni).

Kezdem hátulról - nézzük a JavaScript kód alapú kommunikációt. Ebben az esetben utasítások soráról beszélünk, például a szerver a kliensnek a következő kis programot küldi le:
var message = document.getElementById('message');
content.innerHTML = 'Hello World!';
content.style.display = 'block';
A program egy adott id-jú elembe belepakol egy szöveget, majd láthatóvá teszi azt a blokkot. Elég összetett programok leküldhetőek a kliensnek, sokmindent megvalósítva. Bár még nem használtam ezt a megoldást, ha minden igaz, akkor a Ruby on Rails-hez elérhető AJAX könyvtár így működik (teljesen tehermentesítve a programozót a JavaScript kódolástól, mindent Ruby-ban lehet leírni, és az generálja a JavaScriptet). Itt kifejezetten arról van szó, hogy a klienst a szerver kézenfogva vezeti, ezt a megoldást főként eseményvezérelt felületek kialakításához tudom elképzelni: a kliens kattint egyet, kitölt valamit, átrendez egy listát, stb., történik egy szerver hívás, majd a visszakapott program például visszajelez (bárhogy), hogy a kért művelet lezajlott. Itt igazából a logika marad a szerver oldalon, a kliens csak a buta végrehajtó szerepet tölti be.

A következő lehetőség a HTML formátum. Itt gyakorlatilag arról van szó, hogy a szerver egy oldalrészletet küld a kliensnek, amit az egy az egyben megjelenít (egy adott oldalrészletet lecserélve, kiegészítve). Ilyet használtam a web.zin.hu (és társai) esetén a lapozáshoz - ez egy nagyon egyszerű megoldás, és kiválóan is tud működni. Van neki még egy hatalmas előnye is: segítségével könnyen beilleszthető egy kis AJAX feeling meglévő keretrendszerünkbe, amennyiben az támogatja az oldal egyes részeinek külön "renderelését". Ez a megoldás leginkább lapozás, szűrés, frissítés és ezekhez hasonló tevékenységekhez lehet hasznos, ahol a kliensnek sok dolga nincs. Itt a kliens oldali logika az előző megoldáshoz hasonlóan szintén a nullához közelít. A HTML leküldését szokás AJAH (Asynchronous Javascript and HTML) néven illetni.

Mind a két eddig megoldásra jellemző, hogy inkább a hagyományos weblapok felturbózására, gördülékenyebben történő működtetésére lehet kiváló megoldás, és hogy viszonylag nagyobb sávszélességet igényel. A JavaScript csak egy kiszolgáló, a megjelenítési réteghez tartozó eszköz, még akkor is, ha összetettebb tevékenységeket is végez. Lehet persze okosan elkezdeni turbózni ezeket a megoldásokat, például gyorsítótárazni a kapott HTML-t, és nem kérni le még egyszer, de alapvetően nem fog a helyzet megváltozni. Ha a JavaScriptbe logikát is szeretnénk csepmészni, akkor nem elég kódot vagy HTML-t küldeni neki, mindenképpen valamilyen adatleíró formátumot kell választani. Nos, erre alkalmas az XML és a JSON.

A JSON-ról magáról most nem írnék sokat, aki szeretne, utánanéz majd, hogy pontosan miről van szó. Röviden a lényeg: ez az a formátum, melyben JavaScriptben adatot írhatunk le, így az egyik hatalmas előnye, hogy a JavaScript számára közvetlenül feldolgozható. A közvetlenül feldolgozható azt jelenti, hogy natív változóként jelenik meg az adat, így egy ciklusnak része lehet (iteráció), közvetlenül megcímezhető és lekérdezhető (például data.settings.size), továbbá minden más hasonló előnnyel is rendelkezik, ami ezekből adódik.

A JSON leküldésnek van egy hátránya, miszerint ha hatékonyak akarunk lenni, a leküldött adathalmazt egy eval segítségével "végre kell hajtanunk". Ez egyesek szerint biztonsági kockázat lehet, szerintem viszont a JSON adatot biztonságos forrásból, a saját szerverünkről kell beszerezni (máshonnan elvileg nem is tudja az ember), és akkor már nem. Persze figyeljünk oda, ahogy egy adatbázis beszúrásnál is oda kell figyelni az escape-elésre. Ha nem akarunk hatékonyak lenni, akkor tudjuk validálni a JSON-t egy regexp segítségével, illetve léteznek olyan JS könyvtárak, melyek segítségével "karakterről-karakterre" feldolgozva, eval nélkül is beszerezhető az adat. A hatékonyság viszont nagyon fontos dolog a böngészőben futó JavaScriptnél.

Ahogy írtam, ha a JSON segítségével megvan az adat, elég hatékonyan és gyorsan felhasználható, szinte bármire. Lehet benne a szerver felől parancsokat küldeni, lehet kis adatrészleteket, vagy akár teljes táblázatokat is. Sokan, sokféleképpen használják ezt a formátumot. Előnye minden bizonnyal az, hogy JavaScript műveletekkel könnyen feldolgozható, átalakítható, végrehajtható. Részemről használtam már parancsok átviteli közegeként egy IRC szerver felől, felhasználói beállítások lekérdezésére, tárolására, adatok leküldésére táblázatos adatok megjelenítéséhez, szűréséhez, stb.

A JSON-nak van egy JSONP nevű másik vállfaja, melynek segítségével távoli szerverekről is leszedhetünk adatokat. A trükk az, hogy valójában egy folyton változó JavaScript részletet töltünk le, egy script elem dinamikus hozzáadásával az oldalhoz. Na, ez valójában tud veszélyes lenni, a lényeg itt is az, hogy az adatot megbízható forrásból szerezzük be. A delicious, ami kínál ilyen formájú adathozzáférést is, számomra például ilyen.

Térjünk át az XML-re. Az XML a böngészőkben futó JavaScript nyelvnek nem kifejezetten natív formátuma, mégis elmondható, hogy manapság már elég sok böngésző alapból támogatja így, vagy úgy, s hogy kialakultak egységes JS API-k a kezelésére. Az XML segítéségével szintén összetett adathalmazokat le tudunk küldeni a kliensnek, s azt kényünkre kedvünkre át is tudjuk alakítani XSLT segítségével, majd megjeleníteni. Az adat JavaScript segítségével is megcímezhető, így bármely részlethez hozzáférhetünk, és JavaScript logikát is vihetünk a dologba.

A JSON és XML formátum között van egy megközelítésbeli különbség. Ha főként megjeleníteni szeretnénk az adatokat különböző szempontok szerint, akkor az XML erre kiválóan alkalmas formátum lehet, hiszen XSLT használatával könnyen és gyorsan alakítható HTML-lé a letöltött adat. Az XML használata ott kezd kevésbé kényelmes lenni, amikor az XSLT beépített függvényei számára nem megoldható adatfeldolgozó logikát szeretnénk a programunkba építeni. Ha nagy mennyiségű adatot szeretnénk JavaScripttel kinyerni az XML-ből, akkor az elég lassú lesz ugyanis - vagy legalábbis nekem nem nagyon sikerült gyorsan megoldani ezt a feladatot. A JSON-nál a megjelenítés sebessége lehet a kérdéses, de erről a megjelenítésről szóló írásomban már esett szó: elég hatékonyan megvalósítható ez is.

Szerver oldalon mind az XML, mind pedig a JSON előállítása elég könnyű lehet: az előbbinek kijelenthetjük, hogy nagy hagyománya van, az utóbbi pedig nagyon egyszerű formátum, s egyrészt ma már szinte minden nyelvre elérhető megfelelő kiegészítés hozzá, másrészt pedig egyszerű print/echo/write parancsokkal is létrehozható.

Szalai Ferenc még említi a CSV lehetőségét is, illetve igazából bármilyen szöveges formátum szóba jöhet. Ha nincs valami különösebb oka, hogy így kommunikáljunk, javaslom, ne tegyük: a JSON és az XML jóval hatékonyabb tud lenni, szerver oldalon az előállításban pedig nincsen hatalmas különbség.

A téma iránt érdeklődőknek korábbi írásaim elolvasását mindenképpen javaslom, mivel egyrészt kitértem már ott is a kommunikáció bizonyos vetületeire, másrészt pedig mert a JSON formátum iránt pár projekttel a hátam mögött egy kicsit elfogult vagyok, a hozzászólások között azonban megfelelő és minőségi kritikát kaptam ezügyben. Most ennyit sikerült írni a témáról, remélem, azért sikerült újat is mondanom.
 
1

opera + innerHTML

saxus · 2006. Aug. 22. (K), 08.49
Leírnék egy kissebb észrevételt, amit Operában talapasztaltam innerHTML esetén. Ugyan nem AJAX-l kértem le az adatokat, de nem is azon van a lényeg.

Az adatokat előre megkaptam egy JSON-s tömbben, azt kellett táblázatba rendezni. A táblázat létrehozása kliens oldalon történt, meg voltak előre a minták, szimpla cserés módszerrel behelyettesítettem az adatokat, majd a sorokat egyenként összemásoltam és behelyettesítettem a table elementbe.

A problémák Operában kezdődtek, ugyanis eléggé különbözött az eredmény attól, amit vártam. A megoldás az volt, hogy nem a table-hoz innerHTML-lezünk, hanem létrehozunk egy div-t amelybe az egész táblázatot rakjuk és a table elementhez hozzáírjuk a style="table-layout:fixed" -t is. Ez nálam megoldotta a problémát.


Viszont ha egy sort szeretnék szerkeszteni, akkor ugyanúgy nem jó, igaz csak az adott tr elementen bellüli részt rontja el. Ha erre tud valaki megoldást, megkérem, ne tartsa magában.

Egyébként jó cikk.
6

innerHTML

Bártházi András · 2006. Aug. 23. (Sze), 09.33
Csak azt írtad le, hogy nem azt csinálta az Opera, amit vártál. De azt nem tudjuk, hogy mit csinált, és hogy mit vártál. Az innerHTML-es hozzáférés és a string kezelés is lassú tud lenni (főként IE alatt), illetve nem árt tudni azt sem, hogy a <table> elemen belülre nem <tr>-t, hanem például <tbody> elemeket illik tenni.
8

Ez így félreérthető

Jano · 2006. Aug. 23. (Sze), 11.16
illetve nem árt tudni azt sem, hogy a <table> elemen belülre nem <tr>-t, hanem például <tbody> elemeket illik tenni.


Nem azt szeretted volna mondani, hogy a tr elemeket nem közvetlenül a table elemhez kell kapcsolni, hanem tbody/thead-hez és azt table-höz?
9

De

Bártházi András · 2006. Aug. 23. (Sze), 12.39
De azt. Ma nem szólok hozzá inkább többet. :)
10

innerHTML

saxus · 2006. Aug. 24. (Cs), 03.45
Hopsz, pedig eszemben volt, amikor írtam, csak sietnem kellett.

A hibajelenség az volt, hogy szinte az összes CSS és más formázásra szolgáló kódot figyelmen kívül hagyta és a megjelenített adatok szerkezetileg se nagyon hasonlítottak egy táblázatra, például sokszor össze-vissza csúsztak a cellák. Ezen kívül az események (onclick például) szintén nem működtek.

Azt tudom, hogy <tbody> elemet illik tenni, próbáltam azzal és anélkül is. Az eredmény ugyanaz volt, így bennhagytam, mert mégiscsak illik.

Ami a sebességet illeti, nekem korrektnek tűnt. Másrészt így, hogy behelyettesítéses módszert használok, ahelyett, hogy helyben hoznám létre az elementeket, szerintem gyorsabb (bár nem próbáltam ki), ugyanis nagyon sok elemről lenne szó, egyrészt a kódméretet is jelentősen növelné, másrészt az újrafelhasználhatóságot is rontaná -- jelen esetben.
2

X-JSON header

toxin · 2006. Aug. 22. (K), 12.52
mivel kimaradt az általam kedvelt X-JSON header-es példa, és linket se találtam hozzá a neten, ime:


html szakasz

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/XHTML1/DTD/XHTML1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>X-JSON</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2" />
<script src="scripts/prototype.js" type="text/javascript"></script>
<script type="text/javascript">

// simple hello-word example, toxin@2006

// create a basic proto-class, only example
var myAjaxFiller = 

    Class.create();
    myAjaxFiller.prototype = {
        
//        init 
        initialize : function(target){
            this.target = $(target);
            this.fill();            
        },
        
//        ajax communication
        
        fill : function (){
             var myAjax = new Ajax.Request(
                'ajax.php',
                {
                    method: 'post',
                    onFailure : function(response) {alert("OOOPS");},
                    onComplete: this.showResponse.bind(this)
                });
        },
        
//      if we get JSON in X-JSON header, prototype automatically eval it for us :) 
        showResponse : function(transport,objJSON){
            if (objJSON.thisBoolean) this.target.innerHTML = objJSON.thisString;
        }
}

// start this example
window.onload = function(){
    new myAjaxFiller('target');
}

</script>
</head>
<body>

<div id="target"></div>

</body>
</html>

php

<?php

// our php array2x-json mini-convertor
function json($i=array()) {
    $o='';
    foreach ($i as $k=>$v) { $o .= '"'.$k.'":'.$v.','; }
    header('X-JSON: ({'.substr($o,0,-1).'})');die();
}

// our sample php-array
json(array(
    'thisBoolean'=>'true',
    'thisString'=>'"Hello world."'
)); 



?>
http://toxin.hu/weblabor/xjson/ajax.html
http://toxin.hu/weblabor/xjson/ajax_fejl_xjson.zip

műkődés lásd firebug alatt, console/post/header fül

teljes példa (ami tartalmazza a különbőző karakterkódolások miatti problémák megoldásását is) a crossForm-ban (kereső),

nagyorsan ennyi, backToMunka :)))
3

Error: unknown object 'content' in line 2

Anonymous · 2006. Aug. 22. (K), 14.51
javítanám az első kódot, remélem, jól láttam, hogy nem stimmel :)

var message = document.getElementById('message');
message.innerHTML = 'Hello World!';
message.style.display = 'block';
4

JSON == objektum/tömb literál

Hodicska Gergely · 2006. Aug. 22. (K), 15.21
Röviden a lényeg: ez az a formátum, melyben JavaScriptben adatot írhatunk le, így az egyik hatalmas előnye, hogy a JavaScript számára közvetlenül feldolgozható. A közvetlenül feldolgozható azt jelenti, hogy natív változóként jelenik meg az adat, így egy ciklusnak része lehet

Ez itt kicsit ködösre sikerült. A JavaScriptben lehetőségünk van mondjuk szöveg literálhoz hasonlóan, objektum illetve tömb literál használatára. A JSON az ez.


Felhő
7

Valóban

Bártházi András · 2006. Aug. 23. (Sze), 09.37
A lényeg tehát, hogy a JSON segítségével összetett adatszerkezet írható le (az XML-lel is), viszont ennek az adatszerkezetnek a felhasználását a JavaScript natívan támogatja: a JSON által leírt tömböt for ciklusban feldolgozhatjuk, a beépített tömbkezelő utasításokkal műveleteket hajthatunk végre rajta, stb. Ez magától adódik persze, ha valaki tudja, hogy mi az a JSON.
5

ez is használható

Anonymous · 2006. Aug. 22. (K), 15.43
index.html (részlet)

   <script src="prototype.js" type="text/javascript"></script>
   <script src="json.js" type="text/javascript"></script>
   <script src="datahandler.js" type="text/javascript"></script>
datahandler.js (részlet)

  dataHandler = Class.Create();

  dataHandler.prototype = {
    _aData:'',

    initialize:function(_value)
    {
      this._aData = _value;
      document.body.onclick = this.onEvent.bindAsEventListener(this);
    },

    onEvent:function(_evt)
    {
      this.getResponse(this._aData);
    },

    getResponse:function(_value)
    {	
      var _dataHandler = this;
      var _postdata = { key: _value };				

      var _param = {
        method: 'post',
        parameters: _postdata.toJSONString(),
        onLoading: function(t){},
        onSuccess: function(t){
          _dataHandler.doSomethingWithTheResponseData(t.responseText.parseJSON());
        }
      };

      new Ajax.Request('ajaxreq.php', _param);
		
      return false;
    },
    
    doSomethingWithTheResponseData:function(_data) 
    {
      alert(_data.key);
    }
  }

  function siteInit()
  {
    new dataHandler('kérdés');
  }

  Event.observe(window,'load',siteInit);
ajaxreq.php

<?php
  require_once('json.php');
  $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE | SERVICES_JSON_SUPPRESS_ERRORS);
  $input = file_get_contents('php://input', 1000000);
  // a prototype.js biggyeszti oda
  $input = str_replace('&_=','',$input);
  $inval = $json->decode($input);

  /** 
   * itt csinálunk valami szépet a $inval tömbbel, pl:
   *
   * $data = iconv("UTF-8", "ISO-8859-2", urldecode($inval['key']));
   *
   */

  $outval[] = array('key' => 'válasz');
  
  $output = $json->encode($outval);
  // valamiért ha túl gyorsan érkezik, nem lesz jó...
  sleep(1);
  header('Content-type:text/javascript; charset=utf-8');
  print($output);  

?>