Blog webdeveloperski Patryk yarpo Jar

Ajax w oparciu o cookies

Autor wiadomości Kwiecień 8, 2011

Mimo, że często mówiąc Ajax ma się na myśli nierozerwalnie obiekt XMLHttpRequest, to wcale nie jedyna technika asynchronicznego łączenia się z serwerem przez JavaScript.

Niedawno opisywałem przykład zastosowania pływającej ramki jako medium wymiany danych. W tym artykule pokażę, jak wykorzystać do tego celu ciasteczka (ang. cookies).

Zasada działania

  1. Wyślij na serwer dane za pomocą obrazka (obiekt Image odwołujący się do skryptu PHP [server-side]).
  2. Wynik działania skryptu zapisz w ciasteczku.
  3. Sprawdzaj w JS co pewien czas, czy już ustawiono wartość ciasteczka.

Proponowany przeze mnie kod próbuje zachować intefejs obiektu XMLHttpRequest.

Kod

Aktualny kod znajdziesz na google code:

// obiekt symulujacy XMLHttpRequest oparty o cookies
// autor: Patryk yarpo Jar
// data : 08-IV-2011

function XMLHttpCookieRequest()
{
	var oSelf = { readyState : CONST('UNSET') };

	var nAttemptCounter = 0
        ,   sUrl = ''
        ,   bAsync = true
        ,   aHeaders = []
        ,   bXml = false;

	function CONST(k)
	{
		var c = {	UNSET : 0,  OPENED : 1, LOADING : 3, READY : 4,
				COOKIE_NAME : 'yXMLHttpCookieRequest',
				ERROR : 500, ERROR_TXT : 'Server Error',
				MAX_ATTEMPTS : 10, INTERVAL : 100 };
		return c[k];
	}
	function fOpen(method, url, async)
	{
		// only GET method
		sUrl = url;
		bAsync = (false === async) ? false : true;
		oSelf.readyState = CONST('OPENED');
	}
	function fSend(data)
	{
		fReset();

		var i = new Image();
		i.src = sUrl + '?' + data;
		oSelf.readyState = CONST('LOADING');

		if (bAsync)
		{
			setTimeout(fRead, CONST('INTERVAL'));
		}
		else
		{
			alert('Brak wsparcia dla żądań synchronicznych');
		}
	}
	function fStr2Xml(text)
	{
		if (window.ActiveXObject)
		{
			var doc = new ActiveXObject('Microsoft.XMLDOM');
			doc.async = 'false';
			doc.loadXML(text);
		}
		else
		{
			var parser = new DOMParser()
                        ,   doc = parser.parseFromString(text,'text/xml');
		}
		return doc;
	}
	function fReadData(data)
	{
		if (bXml)
		{
			oSelf.responseXml = fStr2Xml(data);
		}
		else
		{
			oSelf.responseText = data;
		}
		oSelf.readyState = CONST('READY');
	}
	function fRead()
	{
		nAttemptCounter++;
		var data = fGetCookie();
		if (false !== data)
		{
			fReadData(data);
			oSelf.onreadystatechange.apply(oSelf);
			return;
		}

		if (nAttemptCounter > CONST('MAX_ATTEMPTS'))
		{
			oSelf.status = CONST('ERROR');
			oSelf.statusText = CONST('ERROR_TXT');
			oSelf.onreadystatechange.apply(oSelf);
		}
		else
		{
			setTimeout(fRead, CONST('INTERVAL'));
		}
	}
	function fReset()
	{
		oSelf.readyState = CONST('UNSET');
		nAttemptCounter = 0;
		fDelCookie();
		delete oSelf.responseText;
		delete oSelf.responseXml;
	}
	function fAbort()
	{
		sUrl = '';
		bAsync = true;
		bXml = false;
		fReset();
	}
	function fDelCookie()
	{
		document.cookie = CONST('COOKIE_NAME') +
						'=; expires=Thu, 01-Jan-70 00:00:01 GMT;';
	}
	function fGetCookie()
	{
		var docCookie = document.cookie.split("; ");
		for (var i=0; i < docCookie.length; i++)
		{
			var piece = docCookie[i].split("=");
			if (piece[0] === CONST('COOKIE_NAME'))
			{
				return unescape(String(piece[1]).replace(/\+/g, " "));
			}
		}
		return false;
	}
	function fSetRequestHeader(k, v)
	{
		aHeaders.push({'header' : k, 'value' : v});
		bXml = ('content-type' === k.toLowerCase() &&
				-1 !== v.toLowerCase().indexOf('xml'));
	}
	function fGetRequestHeader()
	{
		if (0 === arguments.length)
		{
			var headCont = '';
			for(var i = 0; i < aHeaders.length; i++)
			{
				headCont += aHeaders[i].header + ': ' + aHeaders[i].value + '\n';
			}
			return headCont;
		}
		else if (1 === arguments.length)
		{
			for(var header in aHeaders)
			{
				if (header.header.toLowerCase() === arguments[0].toLowerCase())
				{
					return header.header + ': ' + header.value + '\n';
				}
			}
		}
	}
	return oSelf = {
		open : fOpen,
		send : fSend,
		abort : fAbort,
		setRequestHeader : fSetRequestHeader,
		getResponseHeader : fGetRequestHeader,
		getAllResponseHeaders : fGetRequestHeader,
		onreadystatechange : null,
		status : 200,
		statusText : 'OK'
	};
};

Użycie:

Przykłady na svn:

function testXml()
{
	var client = new XMLHttpCookieRequest();
	client.open('GET', 'xml.php');
	client.setRequestHeader('Content-type', 'text/xml');
	client.onreadystatechange = function()
	{
		if (200 === this.status && 4 === this.readyState)
		{
			var xml = this.responseXml
                        ,   info = xml.getElementsByTagName('imie')[0].firstChild.nodeValue + ' ' +
					xml.getElementsByTagName('nazwisko')[0].firstChild.nodeValue;
			alert(info);
		}
	}
	client.send('imie=Patryk&nazwisko=Jar');
}

Zmiany po stronie serwera

Niestety, taka technika wymaga zmiany po stronie serwera :/

Zamiast wyświetlać dane, trzeba je przekazywać do ciasteczka. W przypadku PHP służy do tego `setcookie'.

Przykładowy kod:

<?php
define('EXPIRES_IN_2_MINUTES', time()+60*2);
$data = $_GET['imie'] . ' ' . $_GET['nazwisko'];

setcookie('yXMLHttpCookieRequest', $data, EXPIRES_IN_2_MINUTES);
echo $data;

Ograniczenia

Nie wszystko działa jak w obiekcie XMLHttpRequest. Oto lista ograniczeń:

  1. Można używać tylko metody GET - trudno za pomocą obrazka przesyłać POSTem ;). Można to rozwiązać używając pływającej ramki i formularza, ale o tym pisałem w osobnym artykule.
  2. Nie implementowałem obsługi żądań synchronicznych. Sądzę, że dałoby się to zrobić, ale czy ktokolwiek będzie tego używał? Chyba nie, to raczej ciekawostka 🙂
  3. Nie do końca prawidłowe statusy HTTP. Po 10 próbach odczytania z ciasteczka ustawiany jest status 500 - server internal error. Choć wcale nie musi to być prawdą.
  4. Konieczność zmian po stronie serwera. Można to obejść robić coś takiego:
    <?php
    ...
    echo $data;
    setcookie('ciastko', $data, $exp);
  5. Ciasteczka mają ograniczoną pojemność, a niektórzy nawet je wyłączają.

Sądzę, że są to wystarczające powody, dla których jednak lepiej jest stosować XMLHttpRequest. Warto jednak wiedzieć i pamiętać, bo czasem może się okazać niezastąpione, że istnieją inne sposoby na komunikację z serwerem.

Warto przeczytać:

Komentarze (0) Trackbacks (0)

Brak komentarzy.


Leave a comment

 

Brak trackbacków.