Prelegere din cadrul materiei "Dezvoltarea aplicaţiilor Web cu JavaScript" (Full-Stack Web Development) predată de Dr. Sabin Buraga (oct.2019–feb.2020).
Resurse suplimentare la https://profs.info.uaic.ro/~busaco/teach/courses/staw/web-film.html
14. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Optimizările sunt realizate pe baza unor euristici
(speculative optimizations)
interpretorul colectează informații (profiling data)
despre datele de intrare (input) ale unor operații
aceste informații vor fi utilizate apoi de compilator pentru
a face diverse presupuneri vizând input-urile posibile
23. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Cod JavaScript intern vs.
preluat dintr-un fișier extern
<body>
…
<script type="text/javascript">
alert ("Salut, lume!");
</script>
</body>
<script type="text/javascript" src="http://salutari.info/salut.js">
</script>
remarcă: pentru browser-ele moderne se va specifica
application/javascript în loc de text/javascript
24. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
arhitectura de bază a interpretorului (script engine) JS
inclus într-un navigator Web (A. Zlatkov, 2017)
blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf
blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e
26. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Procesorul JavaScript e responsabil și cu eliminarea
memoriei neutilizate (junk) via operația garbage collection
algoritmul de bază: mark-and-sweep
rulat periodic
javascript.info/garbage-collection
developer.mozilla.org/Web/JavaScript/Memory_Management
29. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Un program JavaScript are acces la arborele DOM
(Document Object Model)
corespunzător documentului HTML
specificații ale Consorțiului Web
varianta în vigoare: DOM 4 (2015)
www.w3.org/TR/dom/
de revăzut materia „Tehnologii Web”
profs.info.uaic.ro/~busaco/teach/courses/web/
30. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Un program JavaScript are acces la arborele DOM
(Document Object Model)
corespunzător documentului HTML
DOM Living Standard – dom.spec.whatwg.org
specific HTML5, în continuă dezvoltare
(cea mai recentă actualizare: 4 noiembrie 2019)
35. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
dom: core
un document HTML și arborele DOM corespunzător
reprezentat via Live DOM Viewer
software.hixie.ch/utilities/js/live-dom-viewer/
în cazul HTML, numele elementelor
sunt disponibile cu litere mari (capitals)
37. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
De asemenea, programele JavaScript au acces
la diverse obiecte oferite de mediul de execuție
pus la dispoziție de browser
e.g., informații privind contextul rulării
(caracteristici ale navigatorului, latența rețelei),
istoricul navigării, fereastra de redare a conținutului,
transfer (a)sincron de date,…
39. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
interface HTMLDocument : Document {
attribute DOMString title; // titlul documentului
readonly attribute DOMString referrer; // adresa resursei ce referă pagina
readonly attribute DOMString domain; // domeniul de care aparține
readonly attribute DOMString URL; // URL-ul absolut al documentului
attribute HTMLElement body; // acces la elementul <body>
readonly attribute HTMLCollection images; // lista tuturor imaginilor
readonly attribute HTMLCollection links; // lista tuturor legăturilor
readonly attribute HTMLCollection forms; // lista tuturor formularelor
attribute DOMString cookie; // acces la cookie-uri
// emite o excepție dacă e asignată o valoare
void open (); // deschide un flux de scriere (alterează DOM-ul curent)
void close (); // închide fluxul de scriere și forțează redarea conținutului
void write (in DOMString text); // scrie un șir de caract. (e.g., cod HTML)
void writeln (in DOMString text); // idem, dar inserează și new line
NodeList getElementsByName (in DOMString numeElement);
// furnizează o listă de elemente conform unui nume de tag
}; interfață specificată în limbajul declarativ WebIDL
50. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
function topLevelNodeAt (nod, top) {
while (nod && nod.parentNode != top)
nod = nod.parentNode;
return nod;
}
function topLevelNodeBefore (nod, top) {
while (!nod.previousSibling && nod.parentNode != top)
nod = nod.parentNode;
return topLevelNodeAt (nod.previousSibling, top);
}
discuție
Ce rol au cele două funcții?
52. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// un element HTML generic
interface HTMLElement : Element {
attribute DOMString id; // identificator asociat elementului
attribute DOMString title; // titlu explicativ
attribute DOMString lang; // limba în care e redactat conținutul
attribute DOMString className; // numele clasei CSS folosite pentru redare
};
// specifică un formular Web
interface HTMLFormElement : HTMLElement {
readonly attribute HTMLCollection elements; // elementele HTML incluse în formular
readonly attribute long length; // numărul câmpurilor formularului
attribute DOMString action; // URI-ul resursei ce procesează datele
attribute DOMString enctype; // tipul MIME de codificare a datelor
// (e.g., application/x-www-form-urlencoded)
attribute DOMString method; // metoda HTTP folosită: GET, POST
void submit(); // trimite date URI-ului definit de ‘action’
};
// o imagine (conținut grafic raster)
interface HTMLImageElement : HTMLElement {
attribute DOMString alt; // text alternativ descriind conținutul grafic
attribute DOMString src; // URI-ul resursei grafice
};
53. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Interfața HTMLCollection modelează o listă de noduri
un nod poate fi accesat folosind un index numeric
sau pe baza unui identificator (e.g., stabilit via atributul id)
interface HTMLCollection {
readonly attribute unsigned long length; // reprezintă lungimea listei
Node item (in unsigned long index); // oferă un nod via un index numeric
Node namedItem (in DOMString name); // furnizează un nod pe baza numelui
};
72. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Suport pentru procesarea proprietăților CSS
actualmente, modificarea proprietăților de stil
se poate realiza via proprietatea HTMLElement.style
// asocierea mai multor stiluri CSS
elem.style.cssText = "color: blue; border: 1px solid #000";
// similar cu linia – are drept efect alterarea arborelui DOM:
elem.setAttribute ("style", "color: blue; border: 1px solid #000;");
developer.mozilla.org/Web/API/CSSStyleDeclaration
73. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Găsirea de noduri via selectori CSS
Selectors API – Level 1
recomandare W3C (2013)
acces la diverse date via selectorii CSS cu metodele
query() queryAll() querySelector() querySelectorAll()
www.w3.org/TR/selectors-api/
74. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Găsirea de noduri via selectori CSS
querySelector (selectori)
furnizează primul element – folosind traversarea
în adâncime în preordine – care se potrivește grupului
de selectori (delimitați de virgulă)
querySelectorAll (selectori)
oferă lista de tip NodeList a tuturor elementelor găsite
75. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Găsirea de noduri via selectori CSS
// toate elementele <li> selectate via CSS (date de tip NodeList)
var elemente = document.querySelectorAll ("ul.menu > li");
for (var i = 0; i < elemente.length; i++) {
prelucrează (elemente.item (i)); // procesăm fiecare nod
}
78. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// instanțiem un obiect TreeWalker pentru a parcurge arborele DOM
let calator = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT, // selectăm doar nodurile de tip element
{ acceptNode: nod => { // ...și le filtrăm (doar <p>, <div> și <strong>)
if (nod.nodeName === 'P' || nod.nodeName === 'DIV' ||
nod.nodeName === 'STRONG') return NodeFilter.FILTER_ACCEPT;
}});
let noduri = [ ];
// baleiem toate nodurile găsite și le plasăm în tabloul 'noduri'
while(calator.nextNode()) noduri.push(calator.currentNode);
// listăm nodurile găsite
let elem = document.getElementById('info');
noduri.forEach(nod => {
// plasăm informațiile în DOM, în <div> înainte de ultimul nod copil
elem.insertAdjacentText('beforeend', nod.outerHTML + "u25CF");
// și la consola browser-ului Web
console.log (`Element ${nod.nodeName}: ${nod.textContent}`);
});
118. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// crearea interfeței Web via DOM
// construim tabelul cu câmpuri de intrare
for (var i = 0; i < 6; i++) {
// adăugăm un rând la tabel
var row = document.querySelector ("table").insertRow (-1);
for (var j = 0; j < 6; j++) {
var letter = String.fromCharCode ("A".charCodeAt (0) + j - 1);
row.insertCell (-1).innerHTML =
i && j ? "<input id='" + letter + i + "'/>" : i || letter;
}
}
119. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
var DATA={}, INPUTS=[].slice.call (document.querySelectorAll ("input"));
INPUTS.forEach ( function(elm) { // pentru orice <input>, preluăm valoarea
elm.onfocus = function (e) {
e.target.value = localStorage[e.target.id] || ""; };
elm.onblur = function (e) {
localStorage[e.target.id] = e.target.value; computeAll(); };
var getter = function () {
// furnizează valoarea (deja stocată în localStorage)
var value = localStorage[elm.id] || "";
if (value.charAt(0) == "=") { // începe cu =, deci e o formulă de evaluat
with (DATA) return eval (value.substring(1));
} else { return isNaN (parseFloat (value)) ? value : parseFloat (value); }
};
Object.defineProperty (DATA, elm.id, { get: getter });
Object.defineProperty (DATA, elm.id.toLowerCase(), { get: getter });
} );
( window.computeAll = function () { // funcție ce evaluează toate celulele
INPUTS.forEach (function (elm) { try { elm.value = DATA[elm.id]; }
catch(e) {} });
} )();
124. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Browser-ul Web oferă obiectul Window
proprietăți importante:
document – referință la documentul (arborele DOM)
pe care obiectul Window îl include
history – acces la istoricul navigării (via obiectul History)
html.spec.whatwg.org/multipage/history.html
location – adresa Web (URL) curentă
navigator – oferă date despre browser (un obiect Navigator)
developer.mozilla.org/Web/API/Navigator
125. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Browser-ul Web oferă obiectul Window
proprietăți importante – continuare:
screen – oferă detalii despre ecran (obiectul Screen)
developer.mozilla.org/Web/API/Screen
localStorage – date persistente stocate local
(obiectul LocalStorage specificat de HTML5)
developer.mozilla.org/Web/API/Window/localStorage
sessionStorage – referință la date vizând sesiunea curentă
(stocate într-un obiect SessionStorage oferit de HTML5)
html.spec.whatwg.org/multipage/webstorage.html
126. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Browser-ul Web oferă obiectul Window
metode utile:
alert() confirm() prompt() postMessage()
focus() blur() find() print()
open() close() stop()
moveBy() moveTo() resizeBy() resizeTo()
scrool() scrollBy() scrollTo()
amănunte: drafts.csswg.org/cssom-view/
127. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Browser-ul Web oferă obiectul Window
evenimente – specificate prin nume prefixat de on
și clasificate ca fiind WindowEventHandlers
(reuniunea evenimentelor definite de interfețele
Window, HTMLBodyElement și HTMLFrameSetElement)
developer.mozilla.org/Web/API/WindowEventHandlers
141. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Obiectul XMLHttpRequest
send ( )
transmite (asincron) date – e.g., JSON, XML etc. –,
spre aplicația/serviciul ce rulează pe server
orice listener (asociat evenimentelor onloadstart,
onprogress, onload, onloadend, ontimeout, onabort, onerror)
trebuie stabilit înainte de a trimite date
157. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
var xhr = new XMLHttpRequest ();
var numbers = document.getElementById ('numbers’);
// tratarea evenimentului de expirare a timpului de așteptare
xhr.ontimeout = function () { numbers.textContent = 'Time-out... :('; };
// tratarea evenimentului de preluare a datelor solicitate unui serviciu
xhr.onload = function () {
if (xhr.readyState === 4) { // am primit datele
if (xhr.status === 200) { // răspuns Ok din partea serverului
// înlocuim spațiile albe cu virgulă și plasăm conținutul
// în cadrul elementului HTML identificat prin 'numbers'
numbers.textContent = xhr.responseText.trim ().replace (/W+/g, ', ');
} else {
numbers.textContent = 'An error occurred: ' + xhr.statusText;
}
}
};
158. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// adresa Web a sursei de date
const URL = 'https://www.random.org/sequences/
?min=1&max=33&col=1&format=plain';
// timpul maxim de așteptare a răspunsului trimis de server
const TIME = 2000;
// deschidem conexiunea
xhr.open ("GET", URL, true);
// stabilim timpul maxim de așteptare a răspunsului
xhr.timeout = TIME;
// nu expediem date
xhr.send (null);
161. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Recurgem la promises pentru a realiza procesări
în funcție de codul de stare HTTP primit
function status(response) {
if (response.status >= 200 && response.status < 300) {
// cererea poate fi rezolvată
return Promise.resolve (response)
} else {
// cererea a fost rejectată
return Promise.reject (new Error (response.statusText))
}
}
162. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
let numbers = document.getElementById ('numbers’);
fetch (URL)
.then (status) // verificăm dacă datele au fost recepționate cu succes
// transformăm obiectul răspunsului în șir de caractere
.then ((response) => response.text ())
// procesăm secvența de numere
.then ((response) => {
// înlocuim spațiile albe cu virgulă și plasăm conținutul
// în cadrul elementului HTML identificat prin 'numbers'
numbers.textContent = response.trim ().replace (/W+/g, ', ');
})
.catch ((error) => { // a survenit o eroare :(
numbers.textContent = 'An error occurred: ' + error;
});
163. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Scenariu:
preluăm caractere de la client – introduse în <textarea> –
și le trimitem asincron prin POST unei aplicații Web
rulând pe server care le expediază înapoi
mesaje vehiculate în format JSON
{ "tasta": "caracter", "data": "secunde" }
tratăm evenimentul keypress pentru a capta tastele
acționate de utilizator
studiu de caz: PostJSON
164. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// codul programului ES6 interpretat de navigatorul Web
// tratăm evenimentul de apăsare a unei taste
const trateazaEveniment = ev => {
// mesajul propriu-zis trimis serverului prin POST
// atunci când survine evenimentul
let msg = `{ "tasta": "${String.fromCharCode (ev.charCode)}",
"data": "${Date.now()}" }`;
// încapsulăm o cerere POST
let request = new Request ('/ajax/post.php', {
method: 'POST',
body: JSON.stringify (msg), // convertim datele JSON în șir de caractere
headers: {} // n-avem câmpuri-antet
});
de consultat
arhiva cu exemple
165. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
fetch (request) // promitem să executăm codul, transmițând cererea...
.then (response => { // verificăm dacă am primit date JSON de la server
let contentType = response.headers.get ('Content-Type');
if (contentType && contentType.includes ('application/json')) {
return response.json (); };
throw new TypeError ('Datele primite nu-s JSON :(');
})
.then (json => { // procesăm efectiv datele
// creăm un nod text care indică tasta apăsată
let elem = document.createTextNode (json.tasta);
document.getElementById ('tasteApasate').appendChild (elem);
// raportăm datele primite și la consola browser-ului
console.log
(`Date JSON primite: tasta=${json.tasta}, data=${Date(json.data)}`);
})
};
// via DOM, tratăm evenimentul keypress
document.addEventListener ('keypress', trateazaEveniment);
166. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// post.php -- program PHP care preia date JSON
// transmise via POST de client și le trimite înapoi (echo)
function eJSONValid ($sir) { // verifică dacă datele JSON sunt corecte
json_decode ($sir);
return json_last_error () == JSON_ERROR_NONE;
}
// preluăm de la intrarea standard datele transmise de client (raw data)
// (aici, cele dintr-o cerere POST)
$date = trim (file_get_contents ("php://input"));
if (eJSONValid ($date)) { // trimitem datele JSON înapoi dacă sunt în regulă
header ("Content-type: application/json");
echo json_decode ($date);
} else {
die ('Date incorecte’);
}
aplicația (serviciul) Web invocat(ă) pe server
temă: portarea programului pentru Node.js
184. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Șabloane de proiectare AJAX
referitoare la interacțiunea cu utilizatorul:
acțiuni oferite
(Drag-and-Drop, Popup, Upload/Download Files)
detalii la master,
în cadrul materiei Human-Computer Interaction
profs.info.uaic.ro/~busaco/teach/courses/hci/hci-film.html
190. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Comet
permite ca datele să fie „împinse” (push) de către server
spre aplicația client, utilizând conexiuni HTTP
persistente (long-lived) în vederea reducerii latenței
complementar Ajax:
long polling, HTTP server push, Reverse Ajax
194. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Ajax/Comet oferă suport pentru dezvoltarea de
aplicații Web hibride – mash-ups
combinarea – în contextul nostru, la nivel de client –
a conținutului ce provine din surse (situri)
multiple, oferind o funcționalitate/experiență nouă
„curentul” SaaS (Software As A Service)
195. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
mash-ups
Surse de date
(data feeds)
Atom, RSS, geoRSS,
microdate HTML5, RDFa,…
Interfețe de programare
(API-uri)
specifice serviciilor publice
și de procesare JSON, XML etc.
Biblioteci/framework-uri
pentru dezvoltare
framework-uri Web generice
sau oferite de organizații
Instrumente interactive
(Web tools)
eventual, disponibile în „nori”
Platforme
(Platform As A Service)
Digital Ocean, Heroku, Google
Cloud Platform, MS Azure,…
196. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Crearea unui mash-up Web la nivel de client
DoCa (Dogs ‘n’ Cats)
imagini cu câini dog.ceo/dog-api/ – răspuns JSON:
{ "status": "success", "message": "URL_imagine" }
+
fotografii cu pisici aws.random.cat/meow – răspuns JSON:
{ "file": "URL_imagine" }
studiu de caz: DoCa
197. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
codul-sursă complet la jsfiddle.net/busaco/z2f3vp4m/
utilizăm două apeluri fetch()
pentru a prelua date JSON
de la cele două servicii Web
(API-uri publice)
URL-urile obținute sunt
folosite pentru a genera
cu DOM elementele <img />
corespunzătoare
199. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// verificăm dacă avem succes (codul de stare e 2XX)
function status(response) {
if (response.status >= 200 && response.status < 300) {
// cererea poate fi rezolvată
return Promise.resolve (response)
} else {
// cererea a fost rejectată
return Promise.reject (new Error (response.statusText))
}
}
// generăm în cadrul unui element identificat prin 'id'
// elementul <img /> pentru a include URL-ul fotografiei
// (parametrul 'url' dat funcției) ce reprezintă un animal
function genImg (url, id) {
const elem = document.createElement ('img');
elem.setAttribute ('src', url);
document.getElementById (id).appendChild (elem);
}
200. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
const URLDOGS = 'https://dog.ceo/api/breed/dalmatian/images/random';
// folosim Dog API pentru a obține o imagine aleatorie
// a unui câine dalmațian
fetch (URLDOGS)
.then (status) // datele au fost recepționate cu succes?
.then (response => response.json ())
.then (json => {
// procesăm datele JSON primite...
// proprietatea 'message' stochează URL-ul imaginii câinelui
genImg (json.message, 'dog');
})
.catch (error => { // redăm eroarea survenită
document.getElementById ('dog').textContent = error;
});
201. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
// similar pentru pisici…
const URLCATS = 'https://aws.random.cat/meow';
// invocăm random.cat API pentru a obține fotografia unei/unor pisici
fetch (URLCATS)
.then (status) // datele au fost recepționate cu succes?
.then (response => response.json ())
.then (json => {
// procesăm datele JSON primite...
// proprietatea 'file' stochează URL-ul fotografiei cu pisici
genImg (json.file, 'cat');
})
.catch (error => { // redăm eroarea survenită
document.getElementById ('cat').textContent = error;
});
203. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
performanță: scalabilitatea și latența
limite ale API-urilor + existența versiunilor multiple
drepturi de autor asupra datelor și licențiere
securitate: abuz, confidențialitate, încredere etc.
monetizare
lipsa unei interoperabilități reale între platforme
mash-ups
207. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
se permit doar transferuri vizând reprezentări de resurse
referitoare la imagini, fișiere CSS
și alte programe JavaScript aparținând aceleași origini
client
server
Web
HTTP
JSON, XML,…
API
public
API
public
HTTP
JPG
HTTP
JPG
Same-Origin Policy
209. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
interactiune web: ajax – studiu de cazconst URL = "https://profs.info.uaic.ro/~busaco/teach/courses/web/";
// realizăm o cerere HEAD pentru a obține meta-date despre o resursă
let client = new XMLHttpRequest ();
client.open ("HEAD", URL, true);
client.send ();
client.onreadystatechange = function () {
// am recepționat câmpurile-antet?
if (client.readyState == 2) {
// semnalăm tipul MIME și data ultimei actualizări
alert ("Resursa de tip '" +
client.getResponseHeader ("Content-Type") + "' s-a actualizat la " +
client.getResponseHeader ("Last-Modified"));
}
}
preluarea cu HEAD a unor
meta-date, în mod asincron
212. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
CORS (Cross-Origin Resource Sharing)
permite partajarea la nivel de client a resurselor
provenind din domenii Internet diferite
astfel, se pot emite cereri între domenii (cross-origin)
recomandare a Consorțiului Web (2014)
www.w3.org/TR/cors/
213. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Politicile de partajare a resurselor se bazează pe
anumite câmpuri din antetul mesajelor HTTP
exemplu (se permit doar cereri GET
având în antetul mesajului câmpuri specifice din partea
unui client aparținând unui domeniu Internet):
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type, Accept
Origin: http://localhost:8000
resurse de interes:
developer.mozilla.org/en-US/docs/Web/HTTP/CORS
auth0.com/blog/cors-tutorial-a-guide-to-cross-origin-resource-sharing/
215. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Google Play acceptă cereri POST emise numai de serviciul
Google Drive (originea este https://drive.google.com)
ce includ câmpuri-antet vizând autorizarea
Access-Control-Request-Method: POST
Origin: https://drive.google.com
Access-Control-Request-Headers: authorization,x-goog-authuser
câmp nestandardizat
caz real #2
216. Dr.SabinBuragaprofs.info.uaic.ro/~busaco
Reddit.com permite cereri POST și OPTIONS formulate de
orice origine, fiind acceptate anumite câmpuri-antet
(controlul accesului depinde de timp)
Access-Control-Allow-Headers: Content-Type,Origin,Accept,X-Signature
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Max-age: 1728000
caz real #3