XMLHttpRequest – bu JavaScript da HTTP so’rovlar qilish imkonini beruvchi o’rnatilgan brauzer obyekti.
Nomida “XML” so’zi bo’lishiga qaramay, u nafaqat XML formatida, balki har qanday ma’lumotlar bilan ishlashi mumkin. Biz fayllarni yuklash/yuklab olish, jarayonni kuzatish va boshqa ko’p narsalarni qilishimiz mumkin.
Hozirda XMLHttpRequest ni bir oz eskirgan holda qoldiradigan boshqa, zamonaviyroq fetch metodi mavjud.
Zamonaviy veb-ishlanmada XMLHttpRequest uch sabab bilan ishlatiladi:
- Tarixiy sabablar: biz
XMLHttpRequestbilan mavjud script’larni qo’llab-quvvatlashimiz kerak. - Biz eski brauzerlarni qo’llab-quvvatlashimiz va polyfill’lar ishlatishni xohlamaymiz (masalan, script’larni kichik saqlash uchun).
- Bizga
fetchhali qila olmaydigan narsa kerak, masalan yuklash jarayonini kuzatish.
Bu tanish tuyuladimi? Agar ha bo’lsa, XMLHttpRequest bilan davom eting. Aks holda, Fetch ga o’ting.
Asoslar
XMLHttpRequest ikkita ishlash rejimiga ega: sinxron va asinxron.
Avval asinxronni ko’raylik, chunki u ko’pchilik hollarda ishlatiladi.
So’rov qilish uchun bizga 3 qadam kerak:
-
XMLHttpRequestyaratish:let xhr = new XMLHttpRequest();Konstruktor hech qanday argumentga ega emas.
-
Uni ishga tushirish, odatda
new XMLHttpRequestdan keyin darhol:xhr.open(method, URL, [async, user, password])Bu metod so’rovning asosiy parametrlarini belgilaydi:
method– HTTP-metod. Odatda"GET"yoki"POST".URL– so’rov qilinadigan URL, string, URL obyekti bo’lishi mumkin.async– agar aniq ravishdafalsega o’rnatilsa, so’rov sinxron bo’ladi, buni biroz keyinroq ko’rib chiqamiz.user,password– asosiy HTTP auth uchun login va parol (agar kerak bo’lsa).
Diqqat qiling,
openchaqiruvi, nomiga qaramay, ulanishni ochmaydi. U faqat so’rovni sozlaydi, lekin tarmoq faolligi faqatsendchaqiruvi bilan boshlanadi. -
Uni yuborish.
xhr.send([body])Bu metod ulanishni ochadi va so’rovni serverga yuboradi. Ixtiyoriy
bodyparametri so’rov tanasini o’z ichiga oladi.GETkabi ba’zi so’rov metodlari tanaga ega emas.POSTkabi ba’zilari esa ma’lumotlarni serverga yuborish uchunbodydan foydalanadi. Buning misollarini keyinroq ko’ramiz. -
Javob uchun
xhrevent’larini tinglash.Bu uchta event eng keng qo’llaniladi:
load– so’rov tugallanganda (HTTP status 400 yoki 500 bo’lsa ham), va javob to’liq yuklab olinganda.error– so’rov qilinmagan bo’lsa, masalan tarmoq ishlamay qolganda yoki noto’g’ri URL.progress– javob yuklanayotganda vaqti-vaqti bilan ishga tushadi, qancha yuklanganini xabar beradi.
xhr.onload = function() { alert(`Yuklandi: ${xhr.status} ${xhr.response}`); }; xhr.onerror = function() { // faqat so'rov umuman qilinmagan bo'lsa ishga tushadi alert(`Tarmoq Xatosi`); }; xhr.onprogress = function(event) { // vaqti-vaqti bilan ishga tushadi // event.loaded - necha bayt yuklab olingan // event.lengthComputable = agar server Content-Length header yuborgan bo'lsa true // event.total - umumiy baytlar soni (agar lengthComputable bo'lsa) alert(`${event.total} dan ${event.loaded} qabul qilindi`); };
Mana to’liq misol. Quyidagi kod serverdan /article/xmlhttprequest/example/load URL’ini yuklaydi va jarayonni chop etadi:
// 1. Yangi XMLHttpRequest obyektini yaratish
let xhr = new XMLHttpRequest();
// 2. Uni sozlash: /article/.../load URL uchun GET-so'rov
xhr.open('GET', '/article/xmlhttprequest/example/load');
// 3. So'rovni tarmoq orqali yuborish
xhr.send();
// 4. Bu javob olingandan keyin chaqiriladi
xhr.onload = function() {
if (xhr.status != 200) { // javobning HTTP statusini tahlil qilish
alert(`Xato ${xhr.status}: ${xhr.statusText}`); // masalan 404: Not Found
} else { // natijani ko'rsatish
alert(`Tayyor, ${xhr.response.length} bayt olindi`); // response - server javobi
}
};
xhr.onprogress = function(event) {
if (event.lengthComputable) {
alert(`${event.total} dan ${event.loaded} bayt qabul qilindi`);
} else {
alert(`${event.loaded} bayt qabul qilindi`); // Content-Length yo'q
}
};
xhr.onerror = function() {
alert("So'rov muvaffaqiyatsiz");
};
Server javob bergandan keyin, biz natijani quyidagi xhr xususiyatlarida olishimiz mumkin:
status- HTTP status kodi (raqam):
200,404,403va boshqalar, HTTP bo’lmagan muvaffaqiyatsizlik holatida0bo’lishi mumkin. statusText- HTTP status xabari (string): odatda
200uchunOK,404uchunNot Found,403uchunForbiddenva boshqalar. response(eski script’larresponseTextdan foydalanishi mumkin)- Server javob tanasi.
Shuningdek, tegishli xususiyat yordamida timeout belgilashimiz mumkin:
xhr.timeout = 10000; // ms da timeout, 10 soniya
Agar so’rov berilgan vaqt ichida muvaffaqiyatli bo’lmasa, u bekor qilinadi va timeout eventi ishga tushadi.
URL ga ?name=value kabi parametrlar qo’shish va to’g’ri kodlashni ta’minlash uchun URL obyektidan foydalanishimiz mumkin:
let url = new URL('https://google.com/search');
url.searchParams.set('q', 'test me!');
// 'q' parametri kodlangan
xhr.open('GET', url); // https://google.com/search?q=test+me%21
Javob Turi
Javob formatini belgilash uchun xhr.responseType xususiyatidan foydalanishimiz mumkin:
""(standart) – string sifatida olish,"text"– string sifatida olish,"arraybuffer"–ArrayBuffersifatida olish (ikkilik ma’lumotlar uchun, ArrayBuffer, binary arraylar bobiga qarang),"blob"–Blobsifatida olish (ikkilik ma’lumotlar uchun, Blob bobiga qarang),"document"– XML hujjat (XPath va boshqa XML metodlaridan foydalanish mumkin) yoki HTML hujjat sifatida olish (olingan ma’lumotlarning MIME turiga asoslanib),"json"– JSON sifatida olish (avtomatik parse qilinadi).
Masalan, javobni JSON sifatida olaylik:
let xhr = new XMLHttpRequest();
xhr.open('GET', '/article/xmlhttprequest/example/json');
xhr.responseType = 'json';
xhr.send();
// javob {"message": "Hello, world!"}
xhr.onload = function() {
let responseObj = xhr.response;
alert(responseObj.message); // Hello, world!
};
Eski script’larda xhr.responseText va hatto xhr.responseXML xususiyatlarini ham topishingiz mumkin.
Ular tarixiy sabablarga ko’ra mavjud, string yoki XML hujjat olish uchun. Hozirda biz formatni xhr.responseType da o’rnatish va yuqorida ko’rsatilganidek xhr.response olish kerak.
Tayyor holatlar
XMLHttpRequest jarayon davomida holatlar o’rtasida o’zgaradi. Joriy holat xhr.readyState sifatida mavjud.
Spetsifikatsiyadagi barcha holatlar:
UNSENT = 0; // boshlang'ich holat
OPENED = 1; // open chaqirilgan
HEADERS_RECEIVED = 2; // javob header'lari olingan
LOADING = 3; // javob yuklanmoqda (ma'lumot paketi olingan)
DONE = 4; // so'rov tugallangan
XMLHttpRequest obyekti ularni 0 → 1 → 2 → 3 → … → 3 → 4 tartibida bosib o’tadi. Holat 3 tarmoq orqali har safar ma’lumot paketi olinganda takrorlanadi.
Biz ularni readystatechange event yordamida kuzatib borishimiz mumkin:
xhr.onreadystatechange = function() {
if (xhr.readyState == 3) {
// yuklanmoqda
}
if (xhr.readyState == 4) {
// so'rov tugallangan
}
};
Siz readystatechange listener’larini haqiqatan ham eski kodda topishingiz mumkin, u tarixiy sabablarga ko’ra mavjud, chunki load va boshqa event’lar bo’lmagan vaqt bor edi. Hozirda load/error/progress handler’lari uni eskirgan holga keltiradi.
So’rovni to’xtatish
Biz so’rovni istalgan vaqtda to’xtatishimiz mumkin. Buning uchun xhr.abort() chaqiruvi:
xhr.abort(); // so'rovni to'xtatish
Bu abort event’ini ishga tushiradi va xhr.status 0 bo’ladi.
Sinxron so’rovlar
Agar open metodida uchinchi parametr async false ga o’rnatilsa, so’rov sinxron amalga oshiriladi.
Boshqacha qilib aytganda, JavaScript ijrosi send() da to’xtaydi va javob olinganidan keyin davom etadi. alert yoki prompt buyruqlari kabi.
Mana qayta yozilgan misol, open ning 3-parametri false:
let xhr = new XMLHttpRequest();
xhr.open('GET', '/article/xmlhttprequest/hello.txt', false);
try {
xhr.send();
if (xhr.status != 200) {
alert(`Xato ${xhr.status}: ${xhr.statusText}`);
} else {
alert(xhr.response);
}
} catch(err) { // onerror o'rniga
alert("So'rov muvaffaqiyatsiz");
}
Bu yaxshi ko’rinishi mumkin, lekin sinxron chaqiruvlar kamdan-kam ishlatiladi, chunki ular yuklash tugagunicha sahifadagi JavaScript’ni bloklaydi. Ba’zi brauzerlarda scroll qilish imkonsiz bo’ladi. Agar sinxron chaqiruv juda ko’p vaqt olsa, brauzer “osilib qolgan” veb-sahifani yopishni taklif qilishi mumkin.
XMLHttpRequest ning ko’plab ilg’or imkoniyatlari, masalan boshqa domendan so’rov qilish yoki timeout belgilash, sinxron so’rovlar uchun mavjud emas. Shuningdek, ko’rib turganingizdek, progress ko’rsatkichi yo’q.
Bularning barchasiga ko’ra, sinxron so’rovlar juda kam, deyarli hech qachon ishlatilmaydi. Biz ular haqida endi gapirmaymiz.
HTTP-header’lar
XMLHttpRequest maxsus header’larni yuborish va javobdan header’larni o’qish imkonini beradi.
HTTP-header’lar uchun 3 ta metod bor:
setRequestHeader(name, value)-
Berilgan
namevavaluebilan so’rov header’ini o’rnatadi.Masalan:
xhr.setRequestHeader('Content-Type', 'application/json');Header’lar cheklovlariBir nechta header’lar faqat brauzer tomonidan boshqariladi, masalan
ReferervaHost. To’liq ro’yxat spetsifikatsiyada.XMLHttpRequestga foydalanuvchi xavfsizligi va so’rov to’g’riligi uchun ularni o’zgartirish ruxsat etilmagan.Header’ni o’chirib bo’lmaydiXMLHttpRequestning yana bir xususiyati shundaki,setRequestHeaderni bekor qilib bo’lmaydi.Header o’rnatilgandan keyin, u o’rnatilgan. Qo’shimcha chaqiruvlar header’ga ma’lumot qo’shadi, uni qayta yozmaydi.
Masalan:
xhr.setRequestHeader('X-Auth', '123'); xhr.setRequestHeader('X-Auth', '456'); // header quyidagicha bo'ladi: // X-Auth: 123, 456 getResponseHeader(name)-
Berilgan
namebilan javob header’ini oladi (Set-CookievaSet-Cookie2bundan mustasno).Masalan:
xhr.getResponseHeader('Content-Type') getAllResponseHeaders()-
Set-CookievaSet-Cookie2bundan mustasno, barcha javob header’larini qaytaradi.Header’lar bitta qator sifatida qaytariladi, masalan:
Cache-Control: max-age=31536000 Content-Length: 4260 Content-Type: image/png Date: Sat, 08 Sep 2012 16:53:16 GMTHeader’lar orasidagi qator uzilishi har doim
"\r\n"(OS ga bog’liq emas), shuning uchun biz uni alohida header’larga osonlik bilan bo’lishimiz mumkin. Nom va qiymat o’rtasidagi ajratuvchi har doim ikki nuqta va bo’shliq": ". Bu spetsifikatsiyada belgilangan.Shunday qilib, agar biz nom/qiymat juftligi bilan obyekt olishni istasak, biroz JS qo’shishimiz kerak.
Masalan (agar ikkita header bir xil nomga ega bo’lsa, keyingisi avvalgisini qayta yozadi deb faraz qilib):
let headers = xhr .getAllResponseHeaders() .split('\r\n') .reduce((result, current) => { let [name, value] = current.split(': '); result[name] = value; return result; }, {}); // headers['Content-Type'] = 'image/png'
POST, FormData
POST so’rovi qilish uchun biz o’rnatilgan FormData obyektidan foydalanishimiz mumkin.
Sintaksis:
let formData = new FormData([form]); // obyekt yaratadi, ixtiyoriy ravishda <form> dan to'ldiradi
formData.append(name, value); // maydon qo'shadi
Biz uni yaratamiz, ixtiyoriy ravishda formdan to’ldiramiz, kerak bo’lsa ko’proq maydonlar append qilamiz, so’ngra:
xhr.open('POST', ...)–POSTmetodidan foydalanish.xhr.send(formData)formani serverga yuborish.
Masalan:
<form name="person">
<input name="name" value="John">
<input name="surname" value="Smith">
</form>
<script>
// FormData ni formdan oldindan to'ldirish
let formData = new FormData(document.forms.person);
// yana bir maydon qo'shish
formData.append("middle", "Lee");
// uni yuborish
let xhr = new XMLHttpRequest();
xhr.open("POST", "/article/xmlhttprequest/post/user");
xhr.send(formData);
xhr.onload = () => alert(xhr.response);
</script>
Forma multipart/form-data kodlash bilan yuboriladi.
Yoki, agar biz JSON ni ko’proq yoqtirsak, JSON.stringify qilib, string sifatida yuboramiz.
Faqat Content-Type: application/json header’ini o’rnatishni unutmang, ko’plab server-side framework’lari JSON ni u bilan avtomatik dekodlaydi:
let xhr = new XMLHttpRequest();
let json = JSON.stringify({
name: "John",
surname: "Smith"
});
xhr.open("POST", '/submit')
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
xhr.send(json);
.send(body) metodi juda omnivor (hamma yeydi). U Blob va BufferSource obyektlari kabilarni qo’shganda deyarli har qanday body ni yuborishi mumkin.
Yuklash jarayoni
progress eventi faqat yuklab olish bosqichida ishga tushadi.
Ya’ni: agar biz biror narsani POST qilsak, XMLHttpRequest avval bizning ma’lumotlarimizni (so’rov tanasi) yuklaydi, so’ngra javobni yuklab oladi.
Agar biz katta narsani yuklayotgan bo’lsak, yuklash jarayonini kuzatish ancha qiziq. Lekin xhr.onprogress bu yerda yordam bermaydi.
Faqat yuklash event’larini kuzatish uchun metodlarsiz boshqa obyekt mavjud: xhr.upload.
U xhr ga o’xshash event’lar yaratadi, lekin xhr.upload ularni faqat yuklashda ishga tushiradi:
loadstart– yuklash boshlandi.progress– yuklash davomida vaqti-vaqti bilan ishga tushadi.abort– yuklash to’xtatildi.error– HTTP bo’lmagan xato.load– yuklash muvaffaqiyatli tugallandi.timeout– yuklash vaqti tugadi (timeoutxususiyati o’rnatilgan bo’lsa).loadend– yuklash muvaffaqiyat yoki xato bilan tugallandi.
Handler’lar misoli:
xhr.upload.onprogress = function(event) {
alert(`${event.total} dan ${event.loaded} bayt yuklandi`);
};
xhr.upload.onload = function() {
alert(`Yuklash muvaffaqiyatli tugallandi.`);
};
xhr.upload.onerror = function() {
alert(`Yuklash paytida xato: ${xhr.status}`);
};
Mana haqiqiy hayotdagi misol: jarayon ko’rsatkichi bilan fayl yuklash:
<input type="file" onchange="upload(this.files[0])">
<script>
function upload(file) {
let xhr = new XMLHttpRequest();
// yuklash jarayonini kuzatish
xhr.upload.onprogress = function(event) {
console.log(`${event.total} dan ${event.loaded} yuklandi`);
};
// tugallanishni kuzatish: muvaffaqiyatli yoki yo'q
xhr.onloadend = function() {
if (xhr.status == 200) {
console.log("muvaffaqiyat");
} else {
console.log("xato " + this.status);
}
};
xhr.open("POST", "/article/xmlhttprequest/post/upload");
xhr.send(file);
}
</script>
Cross-origin so’rovlar
XMLHttpRequest fetch bilan bir xil CORS siyosatini ishlatib, cross-origin so’rovlar qilishi mumkin.
Xuddi fetch kabi, u sukut bo’yicha boshqa origin ga cookie va HTTP-authorization yubormaydi. Ularni yoqish uchun xhr.withCredentials ni true ga o’rnating:
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('POST', 'http://anywhere.com/request');
...
Cross-origin header’lar haqida batafsil ma’lumot uchun Fetch: Cross-Origin So'rovlari bobiga qarang.
Xulosa
XMLHttpRequest bilan GET-so’rovning odatiy kodi:
let xhr = new XMLHttpRequest();
xhr.open('GET', '/my/url');
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) { // HTTP xatosi?
// xatoni boshqarish
alert( 'Xato: ' + xhr.status);
return;
}
// javobni xhr.response dan olish
};
xhr.onprogress = function(event) {
// jarayonni xabar qilish
alert(`${event.total} dan ${event.loaded} yuklandi`);
};
xhr.onerror = function() {
// HTTP bo'lmagan xatoni boshqarish (masalan, tarmoq ishlamaydi)
};
Aslida ko’proq event’lar bor, zamonaviy spetsifikatsiya ularni ro’yxatlaydi (hayot sikli tartibida):
loadstart– so’rov boshlandi.progress– javobning ma’lumot paketi keldi, ayni paytdagi butun javob tanasiresponseda.abort– so’rovxhr.abort()chaqiruvi bilan bekor qilindi.error– ulanish xatosi yuz berdi, masalan noto’g’ri domen nomi. 404 kabi HTTP-xatolari uchun sodir bo’lmaydi.load– so’rov muvaffaqiyatli tugallandi.timeout– so’rov timeout tufayli bekor qilindi (faqat o’rnatilgan bo’lsa sodir bo’ladi).loadend–load,error,timeoutyokiabortdan keyin ishga tushadi.
error, abort, timeout va load event’lari bir-birini istisno qiladi. Ulardan faqat bittasi sodir bo’lishi mumkin.
Eng ko’p ishlatiladigan event’lar yuklash tugallandi (load), yuklash muvaffaqiyatsiz (error), yoki biz bitta loadend handler ishlatib, nima bo’lganini ko’rish uchun so’rov obyekti xhr xususiyatlarini tekshirishimiz mumkin.
Biz yana bir event ni ko’rdik: readystatechange. Tarixan, u ancha oldin, spetsifikatsiya barqarorlashishidan oldin paydo bo’lgan. Hozirda uni ishlatish shart emas, biz uni yangi event’lar bilan almashtirishimiz mumkin, lekin uni eski script’larda tez-tez topish mumkin.
Agar biz maxsus ravishda yuklashni kuzatishimiz kerak bo’lsa, xhr.upload obyektidagi bir xil event’larni tinglashimiz kerak.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen…)