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
XMLHttpRequest
bilan 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
fetch
hali 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:
-
XMLHttpRequest
yaratish:let xhr = new XMLHttpRequest();
Konstruktor hech qanday argumentga ega emas.
-
Uni ishga tushirish, odatda
new XMLHttpRequest
dan 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 ravishdafalse
ga 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,
open
chaqiruvi, nomiga qaramay, ulanishni ochmaydi. U faqat so’rovni sozlaydi, lekin tarmoq faolligi faqatsend
chaqiruvi bilan boshlanadi. -
Uni yuborish.
xhr.send([body])
Bu metod ulanishni ochadi va so’rovni serverga yuboradi. Ixtiyoriy
body
parametri so’rov tanasini o’z ichiga oladi.GET
kabi ba’zi so’rov metodlari tanaga ega emas.POST
kabi ba’zilari esa ma’lumotlarni serverga yuborish uchunbody
dan foydalanadi. Buning misollarini keyinroq ko’ramiz. -
Javob uchun
xhr
event’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
,403
va boshqalar, HTTP bo’lmagan muvaffaqiyatsizlik holatida0
bo’lishi mumkin. statusText
- HTTP status xabari (string): odatda
200
uchunOK
,404
uchunNot Found
,403
uchunForbidden
va boshqalar. response
(eski script’larresponseText
dan 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"
–ArrayBuffer
sifatida olish (ikkilik ma’lumotlar uchun, ArrayBuffer, binary arraylar bobiga qarang),"blob"
–Blob
sifatida 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
name
vavalue
bilan so’rov header’ini o’rnatadi.Masalan:
xhr.setRequestHeader('Content-Type', 'application/json');
Header’lar cheklovlariBir nechta header’lar faqat brauzer tomonidan boshqariladi, masalan
Referer
vaHost
. To’liq ro’yxat spetsifikatsiyada.XMLHttpRequest
ga foydalanuvchi xavfsizligi va so’rov to’g’riligi uchun ularni o’zgartirish ruxsat etilmagan.Header’ni o’chirib bo’lmaydiXMLHttpRequest
ning yana bir xususiyati shundaki,setRequestHeader
ni 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
name
bilan javob header’ini oladi (Set-Cookie
vaSet-Cookie2
bundan mustasno).Masalan:
xhr.getResponseHeader('Content-Type')
getAllResponseHeaders()
-
Set-Cookie
vaSet-Cookie2
bundan 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 GMT
Header’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', ...)
–POST
metodidan 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 (timeout
xususiyati 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 tanasiresponse
da.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
,timeout
yokiabort
dan 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…)