Lokalno skladištenje podataka browsera (sessionStorage, localStorage, cookies)
Uvod
Nedugo posle početnog upoznavanja sa web dizajnom i back-end programiranjem, programeri se prirodno susretnu sa potrebom za lokalnim skladištenjem podataka koje browseri koriste za prikaz sajtova.
Od browsera se očekuje da "upamte" da je korisnik prijavljen na određeni sajt, da sačuvaju prethodni sadržaj korpe pri online kupovini, boju pozadine i veličinu fonta koju je korisnik izabrao, kao i mnogo šta još.
Navedene opcije se svakako podrazumevaju (godinama unazad), ali - implementacija navedenih opcija - ne dešava se "sama od sebe".
Da bi korisnici mogli da se služe opcijama koje smo prethodno opisali (kao i drugim srodnim opcijama), potrebno je omogućiti sajtovima da podatke smeštaju na računarima klijenata, preko kukija i lokalnih skladišta podataka.
Razlike između HTTP kukija i lokalnih skladišta
Verujemo da je čitaocima pojam HTTP "kolačića" ili "kukija" (eng. - cookie), dobro poznat iz svakodnevne prakse. U pitanju je način za lokalno skladištenje podataka koje posećeni sajtovi čuvaju za svoje potrebe (ali, preciznije, kao što ćemo videti u nastavku, u pitanju je "jedan od načina").
Sam po sebi, kuki predstavlja string (tekstualni podatak), koji se kreira na serveru, šalje browseru i skladišti lokalno, međutim, u daljoj obradi podataka, kuki postaje deo HTTP zahteva koji se šalju serveru.
Prosto rečeno, svaki put kada se klijent obraća serveru, šalju se i kukiji koje je klijent prethodno primio od servera.
U očima mnogih pojedinaca, kukiji su (praktično i u opštem smislu) sinonim za lokalno skladištenje podataka o sajtovima, međutim, videli smo već da upotreba kukija podrazumeva komunikaciju sa serverom, a u nastavku ćemo videti i to da procedura za čitanje i zapis kukija nije 'skroz trivijalna'.
Ali, pre svega (kao što smo već nagovestili), kukiji nisu jedina opcija.
Za jednostavnije 'zahvate' mogu se koristiti lokalna skladišta browsera kojima se pristupa preko JavaScript-a (na vrlo jednostavan način, bez razmene podataka sa serverom).
Operacije sa lokalnim skladištem browsera preko JavaScript-a
Za sam početak, upoznaćemo se sa korišćenjem lokalnih skladišta na računaru klijenta, za šta se u JavaScript-u koriste objekti sessionStorage
i localStorage
.
Pomenuta dva objekta odlikuju mnoge sličnosti i gotovo identična sintaksa za pozivanje komandi, ali, postoje naravno i razlike (i razlozi zašto se u različitim okolnostima koristiti jedna ili druga opcija):
sessionStorage
- podaci su vezani za sesiju i brišu se pri zatvaranju browseralocalStorage
- podaci ostaju zapisani u lokalnom skladištu browsera sve dok se ne obrišu ručno
Objekat sessionStorage
može se (recimo) koristiti za pamćenje sadržaja tekstualnih polja u situacijama kada nije poželjno da se posle osvežavanja stranice sadržaj izgubi, dok se objekat localStorage
može koristiti za pamćenje veličina fontova, boja elemenata koje je korisnik izabrao (i sl).
Skladištenje podataka
Skladištenje podataka (to jest upis), obavlja se preko funkcije setItem
kojoj se kao argumenti predaju ključ i vrednost (identifikator preko koga je nadalje moguće obraćati se podatku, i sam podatak).
// Za upis u skladište sessionStorage
// (koje je vezano za sesiju):
sessionStorage.setItem("boja_pozadine", "#2244ee");
// Za upis u skladište localStorage
// (lokalno skladište browsera):
localStorage.setItem("boja_pozadine", "#2244ee");
Čitanje podataka
Za čitanje podatka koji je već upisan, koristi se funkcija getItem
kojoj se kao argument predaje ključ (koji je vezan za podatak):
// Za čitanje podataka iz skladišta sessionStorage:
let bojaPozadine = sessionStorage.getItem("boja_pozadine");
// Za čitanje podataka iz skladišta localStorage:
let bojaPozadine = localStorage.getItem("boja_pozadine");
// U oba slučaja, promenljiva bojaPozadine
// dobija vrednost "#2244ee"
Uklanjanje podataka
Za uklanjanje podataka, koriste se sledeće funkcije:
removeItem
(uz predavanje ključa) - ukoliko je potrebno ukloniti određeni pojedinačni podatakclearAll
- ukoliko je potrebno ukloniti sve podatke iz lokalnog skladišta
// Za uklanjanje podataka iz skladišta sessionStorage:
sessionStorage.removeItem("boja_pozadine");
// Za uklanjanje podataka iz skladišta localStorage:
localStorage.removeItem("boja_pozadine");
// U oba slučaja, podatak sa ključem
// bojaPozadine biće uklonjen.
// Ukoliko je potrebno isprazniti skladišta
// podataka u potpunosti, mogu se koristiti
// sledeći kodovi:
sessionStorage.clearAll();
// ... ili ....
localStorage.clearAll();
Podaci smešteni u sessionStorage
ističu po zatvaranju browsera, dok podaci smešteni u localStorage
(osim ako se sami za to 'ne pobrinemo') - ne ističu.
Rad sa kukijima je nešto složeniji.
Pored ključeva i vrednosti, potrebno je (pogotovo u okolnostima kada nas na to obavezuju sigurnosni razlozi), navesti i trenutak isteka, kao i to da li je u pitanju kuki koji se može čitati samo na serveru (a takođe se navodi i putanja na domenu za koju kuki važi).
U nastavku, pozabavićemo se obradom kukija u PHP-u i JavaScript-u.
Operacije sa kukijima u PHP-u
PHP, kao serverski jezik, nudi dobru podršku za kukije i raspolaže svim neophodnim funkcijama za manipulaciju kukijima.
Krenimo redom ...
Kreiranje i ažuriranje kukija
Za kreiranje kukija preko PHP-a, koristi se funkcija setcookie
:
<?php
setcookie(naziv, vrednost, trenutak_isteka, putanja);
?>
Da bismo bolje razumeli šta navedeni argumenti označavaju, pogledaćemo primer (objašnjenja su u komentarima):
<?php
$boja_pozadine = "#44ee11";
setcookie("boja_pozadine", $boja_pozadine, time() + 86400 * 30, "/");
// ARGUMENTI SU:
//
// naziv: naziv po kome se kuki nadalje može
// prepoznavati je - "boja_pozadine"
//
// vrednost: vrednost koja se upisuje u kuki (i koja
// praktično predstavlja najvažniji argument),
// biće vrednost promenljive $boja_pozadine
// ("#44ee11")
//
// trenutak_isteka: trenutak u kome kuki ističe, zadaje se
// u sekundama, najčešće tako što se na
// timestamp koji se očita preko funkcije
// time(), dodaje određeni broj sekundi;
// izlaz funkcije time() odgovara trenutnom
// vremenu, a vrednost 86400 predstavlja
// broj sekundi u jednom danu:
// 86400 = 24 * 60 * 60
// .... što znači da kuki koji smo uzeli za
// primer, ističe za 30 dana od trenutka kada
// se pozove funkcija setcookie()
//
// putanja: "/" - budući da je navedena korena putanja
// servera, kuki praktično važi za ceo domen
//
?>
Za ažuriranje kukija (na primer, ako korisnik promeni boju pozadine i sl), takođe se koristi funkcija setcookie
, uz predavanje novih vrednosti:
<?php
$boja_pozadine = "#332aff";
setcookie("boja_pozadine", $boja_pozadine, time() + 86400 * 30, "/");
// Sve je "isto", ali, ovoga puta se predaje vrednost:
// "#332aff"
?>
Sada je kuki dostupan širom domena (svim stranicama sajta), i može mu se lako pristupiti.
Pristup kukijima
Za pristup kukijima koristi se superglobalna promenljiva $_COOKIE
, sa odgovarajućim indeksima, koji (kao što verovatno i sami pretpostavljate), odgovaraju nazivima kukija koji su zadati pri pozivu funkcije setcookie
.
Naravno, da bi sve imalo pravog smisla, pre nego što se pristupi kukiju - prvo treba proveriti da li kuki (uopšte) postoji, za šta se može koristiti (sada već dobro poznata) funkcija isset
:
<?php
// Podrazumevana boja pozadine je bela:
$boja_pozadine = "#fff";
$poruka = "";
if (isset($_COOKIE['boja_pozadine'])) {
$boja_pozadine = $_COOKIE['boja_pozadine'];
}
echo "<font color='" . $boja_pozadine . "'>Ispis</font>";
?>
Uklanjanje kukija
Kukiji se uklanjaju na jednostavan način (koji je doduše i pomalo idiosinkratičan): poziva se funkcija setcookie
, * ali, kao trenutak isteka zadaje se "trenutak u prošlosti".
<?php
setcookie("boja_pozadine", "", 1, "/");
// naziv: Preko prvog argumenta, određuje se koji kuki
// je predviđen za brisanje
//
// vrednost: Prilično je svejedno koju ćemo vrednost
// upisati, ali, pri brisanju kukija tipično se
// kao vrednost zadaje prazna niska, što dodatno
// sugeriše da je u pitanju operacija uklanjanja
//
// trenutak_isteka: Ako se usmerimo na to da UNIX Timestamp za
// 20.03.2021 u 10h koji je dobijen preko
// funkcije time(), ima vrednost 1616230800
// (naravno, u pitanju je vreme kada je kreiran
// članak koji čitate), jasno je da timestamp sa
// vrednošću 1 označava - "trenutak u prošlosti"
//
// putanja: Kuki važi za sve putanje na domenu
?>
Za kraj odeljka koji se tiče operacija sa kukijima u PHP-u, ostaje nešto što se u praksi radi na samom početku - potrebno je proveriti da li browser (uopšte) dozvoljava kreiranje kukija.
Provera podržanosti kukija u browseru
Iako PHP nema specijalizovanu funkciju za proveru podržanosti kukija u browseru, provera se može obaviti na jednostavan način, u dva koraka:
- prvi korak podrazumeva pokušaj kreiranja kukija sa određenim nazivom
- u drugom koraku, proverava se da li je u prethodnom koraku kuki uspešno kreiran
<?php
setcookie("proba", "proba kukija", time() + 3600, "/");
if (isset($_COOKIE['proba'])) {
echo "Čuvanje kukija je omogućeno.";
// U praksi, u slučaju da je čuvanje kukija
// omogućeno, nepotrebno je o tome
// obaveštavati korisnika
}
else {
echo "Čuvanje kukija nije dozvoljeno!";
// U praksi, u slučaju da čuvanje kukija
// nije omogućeno, potrebno je ipak naći
// elegantniji način da se korisnik o tome
// obavesti i naravno - potrebno je preduzeti
// konkretnije korake
}
?>
Takođe, u prethodnoj skripti, može se koristiti i drugačiji uslov ....
<?php
// ....
if (count($_COOKIE) > 0)
// ....
?>
.... što navodimo (iako je prvi kod više nego adekvatan), pre svega iz razloga što verujemo da će mnogi čitaoci naći bolje i kreativnije načine da iskoriste mogućnost prebrojavanja postojećih kukija. :)
Pošto smo se kroz primere u PHP-u upoznali sa opštim principima čuvanja i obrade kukija, pogledaćemo kako se navedene operacije mogu obavljati u JavaScript-u.
Operacije sa kukijima u JavaScript-u
JavaScript (kao front-end jezik), takođe nudi dobru podršku za rad sa kukijima, ali, oko nekih stvari se moramo potruditi nešto više nego što je to slučaj sa PHP-om.
Najbolje je, ipak, da navedenu situaciju shvatimo kao priliku da se dodatno upoznamo sa JavaScript-om i utvrdimo sve što smo do sada naučili (pogotovo kada je u pitanju rad sa nizovima).
Kreiranje kukija
Za kreiranje kukija preko JavaScript-a, koristi se funkcija cookie
, po sledećem obrascu:
document.cookie = "naziv=vrednost; expires=datum; path=/";
Iako je programski kod naizgled drugačiji, prepoznajemo princip sa kojim smo se upoznali u odeljku o kukijima u PHP-u: pri pozivu funkcije, predaje se naziv kukija, vrednost, vreme isteka i putanja.
Deluje jednostavno, međutim, kada je u pitanju datum, potrebno je uneti nisku (ne može se "na licu mesta" pozivati funkcija koja očitava datum, kao u PHP-u), i potrebno je koristiti specifičan format zapisa (koji ćemo prikazati na primeru timestamp-a koji odgovara početku UTC epohe) ....
Thu, 01 Jan 1970 00:00:00 UTC
Format je jednostavan (sam po sebi), ali, pošto određivanje dana u nedelji ("Thu"), shodno datumu ("01 Jan 1970"), nije nešto što ljudi (tipično) mogu izvesti bez napora (čast izuzecima :)), definisaćemo pomoćnu funkciju preko koje se formatira niska koja predstavlja sadržaj kukija (naravno, uz korišćenje obaveznog formata za zapis datuma):
function formatiranjeKukija(naziv, vrednost, dani, sati, domen) {
let datum = new Date();
datum.setTime(datum.getTime() + (dani * 86400 + sati * 3600) * 1000);
return naziv + "=" + "vrednost; " +
"expires=" + datum.toUTCString() +
((domen != "")? "; path=" + domen : "");
//*/
}
Princip je (kao što smo već nagovestili), skoro isti kao u prethodnom slučaju (kada smo koristili PHP), a izvedba je sledeća:
- objekat klase
Date
(datum
), služi za beleženje datuma - metoda
getTime
upisuje trenutni datum u objekatdatum
- na timestamp koji vrati metoda
getTime
, dodaju se i sledeće vrednosti: dani * 86400
(broj dana * broj sekundi u jednom danu)sati * 3600
(broj sati * broj sekundi u jednom satu)
Zbir poslednje dve vrednosti množi se sa 1000, budući da metoda setTime
zapravo operiše sa milisekundama, a preko metode toUTCString
biće kreirana niska u formatu koji je neophodno koristiti pri kreiranju kukija u JS-u
Ako sada pozovemo sledeći kod:
document.cookie = formatiranjeKukija("boja_pozadine", "#2244ee", 30, 0, "/");
.... novi kuki će biti postavljen.
Pristup kukijima
Pristup kukijima je (reklo bi se), najmanje jednostavna operacija kada je u pitanju rad sa kukijima u JavaScript-u.
Za pristup kukijima, JS omogućava samo to da se preuzmu svi kukiji (odjednom), u obliku niske, preko koda po sledećem obrascu:
let kukiji = document.cookie;
.... pri čemu nastaje niska sledećeg formata (smatraćemo da sajt koristi tri kukija):
"naziv_1=vrednost_1"; naziv_2=vrednost_2; naziv_3=vrednost_3"
Za primer ćemo uzeti da je naredba sa slike #15 vratila sledeći sadržaj:
"korisnik=darth_vader"; id=411; boja_pozadine=#2244ee"
Delovi niske nisu direktno dostupni, međutim, budući da su delovi razdvojeni znakom tačka-zarez, niska se može pretvoriti u niz preko funkcije split
....
let kukiji_niz = kukiji.split(";");
.... posle čega se ne mora više prolaziti kroz nisku, već se može pristupati pojedinačnim elementima (praktično, pojedinačnim kukijima):
[
"korisnik=darth_vader",
" id=411",
" boja_pozadine=#2244ee"
]
Pri svakom obraćanju "pojedinačnim niskama", moguće je (ponovo preko funkcije split
), kreirati pomoćnu promenljivu red
; praktično - niz koji sadrži naziv kukija i pripadajuću vrednost:
let red = kukiji_niz[0].split("=");
// red = [ "korisnik", "darth_vader" ];
red = kukiji_niz[1].split("=");
// red = [ " id", "411" ];
red = kukiji_niz[2].split("=");
// red = [ " boja_pozadine", "#2244ee" ];
Pošto smo se upoznali sa svim detaljima, možemo napraviti jednostavnu funkciju koja će pronaći sadržaj određenog kukija (ili ustanoviti da traženi kuki ne postoji):
function pronalazenjeKukija(naziv) {
let kukiji = document.cookie;
let kukiji_niz = kukiji.split(";");
// kukiji_niz = [
// "naziv_1=vrednost_1",
// " naziv_2=vrednost_2",
// " naziv_3=vrednost_3",
// ....
// " naziv_n=vrednost_n",
// ];
for (let i = 0; i < kukiji_niz.length; i++) {
// kukiji_niz[i] = "naziv_i=vrednost_i";
let red = kukiji_niz[i].trim().split("=");
// red = [ "naziv_i", "vrednost_i" ];
if (red[0] == naziv) {
return red[1];
}
}
return false;
}
Čitaocima ostavljamo da gornju funkciju (koja je sama po sebi sasvim adekvatna), dodatno optimizuju.
Uklanjanje kukija
Za uklanjanje kukija koristi se poznati, jednostavni (ali pomalo 'čudni') princip: poziva se metoda za dodavanje (tj. ažuriranje) kukija, u kojoj se poništava vrednost, i (takođe/ponovo), kao vreme isteka navodi se "trenutak u prošlosti".
Ako pozovemo sledeći kod:
document.cookie = "boja_pozadine=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
.... kuki će biti uklonjen.
Provera podržanosti kukija u browseru
Za razliku od prethodnih operacija, provera podržanosti kukija preko JavaScript-a, krajnje je jednostavna:
if (navigator.cookieEnabled) {
alert("Čuvanje kukija je omogućeno.");
// U praksi, u slučaju da je čuvanje kukija omogućeno,
// nepotrebno je o tome obaveštavati korisnika
}
else {
alert("Čuvanje kukija nije dozvoljeno!");
// U praksi, u slučaju da je čuvanje kukija nije omogućeno,
// potrebno je naći elegantniji način da se korisnik o tome
// obavesti (i potrebno je preduzeti konkretnije korake).
}
Sledeći koraci ....
Na kraju, pošto smo se upoznali sa mehanizmima za "trajno" čuvanje podataka iz browsera na računaru klijenta, možemo preći na sledeći (reklo bi se "prirodan") korak, to jest, primere koji se tipično vezuju za upotrebu lokalnih skladišta browsera.
U pitanju su: autentifikacija korisnika (čime smo se u velikoj meri već pozabavili u tutorijalu za kreiranje forme za prijavu), i autorizacija korisnika - što će biti tema jednog od narednih članaka ....