1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
|
---
title: Использование Fetch
slug: Web/API/Fetch_API/Using_Fetch
translation_of: Web/API/Fetch_API/Using_Fetch
---
<p><a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> предоставляет интерфейс JavaScript для работы с запросами и ответами HTTP. Он также предоставляет глобальный метод {{domxref("GlobalFetch.fetch","fetch()")}}, который позволяет легко и логично получать ресурсы по сети асинхронно.</p>
<p>Подобная функциональность ранее достигалась с помощью {{domxref("XMLHttpRequest")}}. Fetch представляет собой лучшую альтернативу, которая может быть легко использована другими технологиями, такими как {{domxref("ServiceWorker_API", "Service Workers")}}. Fetch также обеспечивает единое логическое место для определения других связанных с HTTP понятий, такие как CORS и расширения для HTTP.</p>
<p>Обратите внимание, <code>fetch</code> спецификация отличается от <code>jQuery.ajax()</code> в основном в двух пунктах:</p>
<ul>
<li>Promise возвращаемый вызовом <code>fetch()</code> <strong>не перейдёт в состояние "отклонено" из-за ответа HTTP, который считается ошибкой</strong>, даже если ответ HTTP 404 или 500. Вместо этого, он будет выполнен нормально (с значением false в статусе <code>ok</code> ) и будет отклонён только при сбое сети или если что-то помешало запросу выполниться.</li>
<li>По умолчанию, <code>fetch</code> <strong>не будет отправлять или получать cookie файлы </strong>с сервера, в результате чего запросы будут осуществляться без проверки подлинности, что приведёт<span class="tlid-translation translation" lang="ru"><span title=""> к неаутентифицированным запросам</span></span>, если сайт полагается на проверку пользовательской сессии (для отправки cookie файлов в аргументе <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters">init options</a> должно быть задано значение свойства <em>credentials</em> отличное от значения по умолчанию <code>omit</code>).</li>
</ul>
<div class="blockIndicator note">
<p>25 августа 2017 г. в спецификации изменилось значение по умолчанию свойства <em>credentials</em> на <code>same-origin</code>. Firefox применяет это изменение с версии 61.0b13.</p>
</div>
<p>Базовый запрос на получение данных действительно прост в настройке. Взгляните на следующий код:</p>
<pre>fetch('http://example.com/movies.json')
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
});</pre>
<p>Здесь мы забираем JSON файл по сети и выводим его содержимое в консоль. Самый простой способ использования <code>fetch()</code> заключается в вызове этой функии с одним аргументом — строкой, содержащей путь к ресурсу, который вы хотите получить — которая возвращает promise, содержащее ответ (объект {{domxref("Response")}}).</p>
<p>Конечно, это просто HTTP-ответ, а не фактический JSON. Чтобы извлечь содержимое тела JSON из ответа, мы используем {{domxref("Body.json","json()")}} метод (определён миксином {{domxref("Body")}}, который реализован в объектах {{domxref("Request")}} и {{domxref("Response")}}.)</p>
<div class="note">
<p><strong>Примечание</strong>: Миксин Body имеет подобные методы для извлечения других типов контента; см. раздел {{anch("Тело")}}.</p>
</div>
<p>Fetch-запросы контролируются посредством директивы <code>connect-src</code> (<a href="/en-US/docs/Security/CSP/CSP_policy_directives">Content Security Policy</a>), а не директивой извлекаемых ресурсов.</p>
<h3 id="Установка_параметров_запроса">Установка параметров запроса</h3>
<p>Метод <code>fetch()</code> может принимать второй параметр - объект <code>init</code>, который позволяет вам контролировать различные настройки:</p>
<pre class="brush: js">// Пример отправки POST запроса:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return await response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then((data) => {
console.log(data); // JSON data parsed by `response.json()` call
});</pre>
<p>С подробным описанием функции и полным списком параметров вы можете ознакомиться на странице {{domxref("GlobalFetch.fetch","fetch()")}}.</p>
<h3 id="Отправка_запроса_с_учётными_данными">Отправка запроса с учётными данными</h3>
<p>Чтобы браузеры могли отправлять запрос с учётными данными (даже для cross-origin запросов), добавьте <code>credentials: 'include'</code> в объект <code>init</code>, передаваемый вами в метод <code>fetch()</code>:</p>
<pre class="brush: js"><code>fetch('https://example.com', {
credentials: 'include'
})</code></pre>
<p>Если вы хотите отправлять запрос с учётными данными только если URL принадлежит одному источнику (origin) что и вызывающий его скрипт, добавьте <code>credentials: 'same-origin'</code>.</p>
<pre><code>// Вызывающий скрипт принадлежит источнику 'https://example.com'
fetch('https://example.com', {
credentials: 'same-origin'
})</code></pre>
<p>Напротив, чтобы быть уверенным, что учётные данные не передаются с запросом, используйте <code>credentials: 'omit'</code>:</p>
<pre class="brush: js"><code>fetch('https://example.com', {
credentials: 'omit'
})</code></pre>
<h3 id="Отправка_данных_в_формате_JSON">Отправка данных в формате JSON</h3>
<p>При помощи {{domxref("GlobalFetch.fetch","fetch()")}} можно отправлять POST-запросы в формате JSON.</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> url <span class="operator token">=</span> <span class="string token">'https://example.com/profile'</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> data <span class="operator token">=</span> <span class="punctuation token">{</span> username<span class="punctuation token">:</span> <span class="string token">'example'</span> <span class="punctuation token">}</span><span class="punctuation token">;</span>
<span class="keyword token">try</span> <span class="punctuation token">{</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span>url<span class="punctuation token">,</span> <span class="punctuation token">{</span>
method<span class="punctuation token">:</span> <span class="string token">'POST'</span><span class="punctuation token">,</span> <span class="comment token">// или 'PUT'</span>
body<span class="punctuation token">:</span> <span class="constant token">JSON</span><span class="punctuation token">.</span><span class="function token">stringify</span><span class="punctuation token">(</span>data<span class="punctuation token">)</span><span class="punctuation token">,</span> <span class="comment token">// данные могут быть 'строкой' или {объектом}!</span>
headers<span class="punctuation token">:</span> <span class="punctuation token">{</span>
<span class="string token">'Content-Type'</span><span class="punctuation token">:</span> <span class="string token">'application/json'</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> json <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">json</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'Успех:'</span><span class="punctuation token">,</span> <span class="constant token">JSON</span><span class="punctuation token">.</span><span class="function token">stringify</span><span class="punctuation token">(</span>json<span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="string token">'Ошибка:'</span><span class="punctuation token">,</span> error<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="Загрузка_файла_на_сервер">Загрузка файла на сервер</h3>
<p>На сервер можно загрузить файл, используя комбинацию HTML-элемента <code><input type="file" /></code>, {{domxref("FormData.FormData","FormData()")}} и {{domxref("WindowOrWorkerGlobalScope/fetch","fetch()")}}.</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> formData <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">FormData</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> fileField <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">querySelector</span><span class="punctuation token">(</span><span class="string token">'input[type="file"]'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
formData<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'username'</span><span class="punctuation token">,</span> <span class="string token">'abc123'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
formData<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'avatar'</span><span class="punctuation token">,</span> fileField<span class="punctuation token">.</span>files<span class="punctuation token">[</span><span class="number token">0</span><span class="punctuation token">]</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">try</span> <span class="punctuation token">{</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span><span class="string token">'https://example.com/profile/avatar'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span>
method<span class="punctuation token">:</span> <span class="string token">'PUT'</span><span class="punctuation token">,</span>
body<span class="punctuation token">:</span> formData
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> result <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">json</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'Успех:'</span><span class="punctuation token">,</span> <span class="constant token">JSON</span><span class="punctuation token">.</span><span class="function token">stringify</span><span class="punctuation token">(</span>result<span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="string token">'Ошибка:'</span><span class="punctuation token">,</span> error<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="Загрузка_нескольких_файлов_на_сервер">Загрузка нескольких файлов на сервер</h3>
<p>На сервер можно загрузить несколько файлов, используя комбинацию HTML-элемента <code><input type="file" multiple /></code>, {{domxref("FormData.FormData","FormData()")}} и {{domxref("WindowOrWorkerGlobalScope/fetch","fetch()")}}.</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> formData <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">FormData</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> photos <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">querySelector</span><span class="punctuation token">(</span><span class="string token">'input[type="file"][multiple]'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
formData<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'title'</span><span class="punctuation token">,</span> <span class="string token">'Мой отпуск в Вегасе'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">for</span> <span class="punctuation token">(</span><span class="keyword token">let</span> i <span class="operator token">=</span> <span class="number token">0</span><span class="punctuation token">;</span> i <span class="operator token"><</span> photos<span class="punctuation token">.</span>files<span class="punctuation token">.</span>length<span class="punctuation token">;</span> i<span class="operator token">++</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
formData<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'photos'</span><span class="punctuation token">,</span> photos<span class="punctuation token">.</span>files<span class="punctuation token">[</span>i<span class="punctuation token">]</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="keyword token">try</span> <span class="punctuation token">{</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span><span class="string token">'https://example.com/posts'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span>
method<span class="punctuation token">:</span> <span class="string token">'POST'</span><span class="punctuation token">,</span>
body<span class="punctuation token">:</span> formData
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> result <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">json</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'Успех:'</span><span class="punctuation token">,</span> <span class="constant token">JSON</span><span class="punctuation token">.</span><span class="function token">stringify</span><span class="punctuation token">(</span>result<span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="string token">'Ошибка:'</span><span class="punctuation token">,</span> error<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="Обработка_текстового_файла_построчно">Обработка текстового файла построчно</h3>
<p>Фрагменты данных, получаемые из ответа, не разбиваются на строки автоматически (по крайней мере с достаточной точностью) и представляют собой не строки, а объекты {{jsxref("Uint8Array","Uint8Array")}}. Если вы хотите загрузить текстовый файл и обрабатывать его по мере загрузки построчно, то на вас самих ложится груз ответственности за обработку всех упомянутых моментов. Как пример, далее представлен один из способов подобной обработки с помощью создания построчного итератора (для простоты приняты следующие допущения: текст приходит в кодировке UTF-8 и ошибки получения не обрабатываются).</p>
<pre class="brush: js">async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder("utf-8");
let response = await fetch(fileURL);
let reader = response.body.getReader();
let {value: chunk, done: readerDone} = await reader.read();
chunk = chunk ? utf8Decoder.decode(chunk) : "";
let re = /\n|\r|\r\n/gm;
let startIndex = 0;
let result;
for (;;) {
let result = re.exec(chunk);
if (!result) {
if (readerDone) {
break;
}
let remainder = chunk.substr(startIndex);
({value: chunk, done: readerDone} = await reader.read());
chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : "");
startIndex = re.lastIndex = 0;
continue;
}
yield chunk.substring(startIndex, result.index);
startIndex = re.lastIndex;
}
if (startIndex < chunk.length) {
//последняя строка не имеет символа перевода строки в конце
yield chunk.substr(startIndex);
}
}
for await (let line of makeTextFileLineIterator(urlOfFile)) {
processLine(line);
}</pre>
<h3 id="Проверка_успешности_запроса">Проверка успешности запроса</h3>
<p>В методе {{domxref("GlobalFetch.fetch","fetch()")}} promise будет отклонён (reject) с {{jsxref("TypeError")}}, когда случится ошибка сети или не будет сконфигурирован CORS на стороне запрашиваемого сервера, хотя обычно это означает проблемы доступа или аналогичные — для примера, 404 не является сетевой ошибкой. Для достоверной проверки успешности <code>fetch()</code> будет включать проверку того, что promise успешен (resolved), затем проверку того, что значение свойства {{domxref("Response.ok")}} является true. Код будет выглядеть примерно так:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">try</span> <span class="punctuation token">{</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span><span class="string token">'flowers.jpg'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span>response<span class="punctuation token">.</span>ok<span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">throw</span> <span class="keyword token">new</span> <span class="class-name token">Error</span><span class="punctuation token">(</span><span class="string token">'Ответ сети был не ok.'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="keyword token">const</span> myBlob <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">blob</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> objectURL <span class="operator token">=</span> <span class="constant token">URL</span><span class="punctuation token">.</span><span class="function token">createObjectURL</span><span class="punctuation token">(</span>myBlob<span class="punctuation token">)</span><span class="punctuation token">;</span>
myImage<span class="punctuation token">.</span>src <span class="operator token">=</span> objectURL<span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'Возникла проблема с вашим fetch запросом: '</span><span class="punctuation token">,</span> error<span class="punctuation token">.</span>message<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="Составление_своего_объекта_запроса">Составление своего объекта запроса</h3>
<p>Вместо передачи пути ресурса, который вы хотите запросить вызовом <code>fetch()</code>, вы можете создать объект запроса, используя конструктор {{domxref("Request.Request","Request()")}}, и передать его в <code>fetch()</code> аргументом:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> myHeaders <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Headers</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> myInit <span class="operator token">=</span> <span class="punctuation token">{</span>
method<span class="punctuation token">:</span> <span class="string token">'GET'</span><span class="punctuation token">,</span>
headers<span class="punctuation token">:</span> myHeaders<span class="punctuation token">,</span>
mode<span class="punctuation token">:</span> <span class="string token">'cors'</span><span class="punctuation token">,</span>
cache<span class="punctuation token">:</span> <span class="string token">'default'</span>
<span class="punctuation token">}</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> myRequest <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Request</span><span class="punctuation token">(</span><span class="string token">'flowers.jpg'</span><span class="punctuation token">,</span> myInit<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span>myRequest<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> myBlob <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">blob</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> objectURL <span class="operator token">=</span> <span class="constant token">URL</span><span class="punctuation token">.</span><span class="function token">createObjectURL</span><span class="punctuation token">(</span>myBlob<span class="punctuation token">)</span><span class="punctuation token">;</span>
myImage<span class="punctuation token">.</span>src <span class="operator token">=</span> objectURL<span class="punctuation token">;</span></code></pre>
<p>Конструктор <code>Request()</code> принимает точно такие же параметры, как и метод <code>fetch()</code>. Вы даже можете передать существующий объект запроса для создания его копии:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> anotherRequest <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Request</span><span class="punctuation token">(</span>myRequest<span class="punctuation token">,</span> myInit<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Довольно удобно, когда тела запроса и ответа используются единожды (прим.пер.: <em>"are one use only"</em>). Создание копии как показано позволяет вам использовать запрос/ответ повторно, при изменении опций <code>init</code>, при желании. Копия должна быть сделана до прочтения тела, а чтение тела в копии также пометит его прочитанным в исходном запросе.</p>
<div class="note">
<p><strong>Примечание</strong>: Также есть метод {{domxref("Request.clone","clone()")}}, создающий копии. Оба метода создания копии прекратят работу с ошибкой если тело оригинального запроса или ответа уже было прочитано, но чтение тела клонированного ответа или запроса не приведёт к маркировке оригинального.</p>
</div>
<h2 id="Заголовки">Заголовки</h2>
<p>Интерфейс {{domxref("Headers")}} позволяет вам создать ваш собственный объект заголовков через конструктор {{domxref("Headers.Headers","Headers()")}}. Объект заголовков - простая мультикарта имён-значений:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> content <span class="operator token">=</span> <span class="string token">'Hello World'</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> myHeaders <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Headers</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
myHeaders<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'Content-Type'</span><span class="punctuation token">,</span> <span class="string token">'text/plain'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
myHeaders<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'Content-Length'</span><span class="punctuation token">,</span> content<span class="punctuation token">.</span>length<span class="punctuation token">.</span><span class="function token">toString</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
myHeaders<span class="punctuation token">.</span><span class="function token">append</span><span class="punctuation token">(</span><span class="string token">'X-Custom-Header'</span><span class="punctuation token">,</span> <span class="string token">'ProcessThisImmediately'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>То же может быть достигнуто путём передачи массива массивов или литерального объекта конструктору:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> myHeaders <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Headers</span><span class="punctuation token">(</span><span class="punctuation token">{</span>
<span class="string token">'Content-Type'</span><span class="punctuation token">:</span> <span class="string token">'text/plain'</span><span class="punctuation token">,</span>
<span class="string token">'Content-Length'</span><span class="punctuation token">:</span> content<span class="punctuation token">.</span>length<span class="punctuation token">.</span><span class="function token">toString</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
<span class="string token">'X-Custom-Header'</span><span class="punctuation token">:</span> <span class="string token">'ProcessThisImmediately'</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Содержимое может быть запрошено и извлечено:</p>
<pre class="brush: js">console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue");
console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.get("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
myHeaders.delete("X-Custom-Header");
console.log(myHeaders.get("X-Custom-Header")); // [ ]</pre>
<p>Некоторые из этих операций могут быть использованы только в {{domxref("ServiceWorker_API","ServiceWorkers")}}, но они предоставляют более удобный API для манипуляции заголовками.</p>
<p>Все методы Headers выбрасывают <code>TypeError</code>, если имя используемого заголовка не является валидным именем HTTP Header. Операции мутации выбросят <code>TypeError</code> если есть защита от мутации (смотрите ниже) (прим.пер.: <em>"if there is an immutable guard"</em>). В противном случае они прерываются молча. Например:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> myResponse <span class="operator token">=</span> Response<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">try</span> <span class="punctuation token">{</span>
myResponse<span class="punctuation token">.</span>headers<span class="punctuation token">.</span><span class="function token">set</span><span class="punctuation token">(</span><span class="string token">'Origin'</span><span class="punctuation token">,</span> <span class="string token">'http://mybank.com'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>e<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'Не могу притвориться банком!'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<p>Хорошим вариантом использования заголовков является проверка корректности типа контента перед его обработкой. Например:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">try</span> <span class="punctuation token">{</span>
<span class="keyword token">const</span> response <span class="operator token">=</span> <span class="keyword token">await</span> <span class="function token">fetch</span><span class="punctuation token">(</span>myRequest<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">const</span> contentType <span class="operator token">=</span> response<span class="punctuation token">.</span>headers<span class="punctuation token">.</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'content-type'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span>contentType <span class="operator token">||</span> <span class="operator token">!</span>contentType<span class="punctuation token">.</span><span class="function token">includes</span><span class="punctuation token">(</span><span class="string token">'application/json'</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">throw</span> <span class="keyword token">new</span> <span class="class-name token">TypeError</span><span class="punctuation token">(</span><span class="string token">"Ой, мы не получили JSON!"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="keyword token">const</span> json <span class="operator token">=</span> <span class="keyword token">await</span> response<span class="punctuation token">.</span><span class="function token">json</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="comment token">/* Дальнейшая обработка JSON */</span>
<span class="punctuation token">}</span> <span class="keyword token">catch</span> <span class="punctuation token">(</span>error<span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span>error<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="Защита">Защита</h3>
<p>С тех пор как заголовки могут передаваться в запросе, приниматься в ответе и имеют различные ограничения в отношении того, какая информация может и должна быть изменена, заголовки имеют свойство guard. Это не распространяется на Web, но влияет на то, какие операции мутации доступны для объекта заголовков.</p>
<p>Возможные значения:</p>
<ul>
<li><code>none</code>: по умолчанию.</li>
<li><code>request</code>: защита объекта заголовков, полученного по запросу ({{domxref("Request.headers")}}).</li>
<li><code>request-no-cors</code>: защита объекта заголовков, полученного по запросу созданного с {{domxref("Request.mode")}} <code>no-cors</code>.</li>
<li><code>response</code>: защита Headers полученных от ответа ({{domxref("Response.headers")}}).</li>
<li><code>immutable</code>: в основном, используется в ServiceWorkers; делает объект заголовков read-only.</li>
</ul>
<div class="note">
<p><strong>Примечание</strong>: Вы не можете добавить или установить <code>request</code> защищаемые Headers’ заголовок <code>Content-Length</code>. Аналогично, вставка <code>Set-Cookie</code> в заголовок ответа недопустимо: ServiceWorkers не допускают установки cookies через синтезированные ответы.</p>
</div>
<h2 id="Объекты_ответа">Объекты ответа</h2>
<p>Как вы видели выше, экземпляр {{domxref("Response")}} будет возвращён когда <code>fetch()</code> промис будет исполнен.</p>
<p>Свойства объекта-ответа которые чаще всего используются:</p>
<ul>
<li>{{domxref("Response.status")}} — Целочисленное (по умолчанию 200) содержит код статуса ответа.</li>
<li>{{domxref("Response.statusText")}} — Строка (по умолчанию"OK"), которая соответствует HTTP коду статуса.</li>
<li>{{domxref("Response.ok")}} — как сказано ранее, это короткое свойство для упрощения проверки на то что статус ответа находится где-то между 200-299 включительно. Это свойство типа {{domxref("Boolean")}}.</li>
</ul>
<p>Они так же могут быть созданы с помощью JavaScript, но реальная польза от этого есть только при использовании {{domxref("ServiceWorker_API", "сервис-воркеров")}}, когда вы предоставляете собственный ответ на запрос с помощью метода {{domxref("FetchEvent.respondWith","respondWith()")}}:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> myBody <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Blob</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="function token">addEventListener</span><span class="punctuation token">(</span><span class="string token">'fetch'</span><span class="punctuation token">,</span> <span class="keyword token">function</span><span class="punctuation token">(</span><span class="parameter token">event</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="comment token">// ServiceWorker перехватывает fetch</span>
event<span class="punctuation token">.</span><span class="function token">respondWith</span><span class="punctuation token">(</span>
<span class="keyword token">new</span> <span class="class-name token">Response</span><span class="punctuation token">(</span>myBody<span class="punctuation token">,</span> <span class="punctuation token">{</span>
headers<span class="punctuation token">:</span> <span class="punctuation token">{</span> <span class="string token">'Content-Type'</span><span class="punctuation token">:</span> <span class="string token">'text/plain'</span> <span class="punctuation token">}</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span>
<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Конструктор {{domxref("Response.Response","Response()")}} принимает два необязательных аргумента — тело для ответа и объект init (аналогичный тому, который принимает {{domxref("Request.Request","Request()")}})</p>
<ul>
</ul>
<div class="note">
<p><strong>Примечание</strong>: Метод {{domxref("Response.error","error()")}} просто возвращает ответ об ошибке. Аналогично, {{domxref("Response.redirect","redirect()")}} возвращает ответ, приводящий к перенаправлению на указанный URL. Они также относятся только к Service Workers.</p>
</div>
<h2 id="Тело">Тело</h2>
<p>Запрос и ответ могут содержать данные тела. Тело является экземпляром любого из следующих типов:</p>
<ul>
<li>{{domxref("ArrayBuffer")}}</li>
<li>{{domxref("ArrayBufferView")}} (Uint8Array и подобные)</li>
<li>{{domxref("Blob")}}/File</li>
<li>string</li>
<li>{{domxref("URLSearchParams")}}</li>
<li>{{domxref("FormData")}}</li>
</ul>
<p>{{domxref("Body")}} примесь определяет следующие методы для извлечения тела (реализованы как для {{domxref("Request")}} так и для {{domxref("Response")}}). Все они возвращают promise, который в конечном итоге исполняется и выводит содержимое.</p>
<ul>
<li>{{domxref("Body.arrayBuffer","arrayBuffer()")}}</li>
<li>{{domxref("Body.blob","blob()")}}</li>
<li>{{domxref("Body.json","json()")}}</li>
<li>{{domxref("Body.text","text()")}}</li>
<li>{{domxref("Body.formData","formData()")}}</li>
</ul>
<p>Это делает использование нетекстовых данных более лёгким, чем при XMR.</p>
<p>В запросе можно установить параметры для отправки тела запроса:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">const</span> form <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">FormData</span><span class="punctuation token">(</span>document<span class="punctuation token">.</span><span class="function token">getElementById</span><span class="punctuation token">(</span><span class="string token">'login-form'</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="function token">fetch</span><span class="punctuation token">(</span><span class="string token">'/login'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span>
method<span class="punctuation token">:</span> <span class="string token">'POST'</span><span class="punctuation token">,</span>
body<span class="punctuation token">:</span> form
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Параметры request и response (and by extension the <code>fetch()</code> function), по возможности возвращают корректные типы данных. Параметр request также автоматически установит <code>Content-Type</code> в заголовок, если он не был установлен из словаря.</p>
<h2 id="Функция_обнаружения">Функция обнаружения</h2>
<p>Поддержка Fetch API может быть обнаружена путём проверки наличия {{domxref("Headers")}}, {{domxref("Request")}}, {{domxref("Response")}} или {{domxref("GlobalFetch.fetch","fetch()")}} в области видимости {{domxref("Window")}} или {{domxref("Worker")}}. Для примера:</p>
<pre class="brush: js">if (self.fetch) {
// запустить мой fetch запрос здесь
} else {
// Сделать что-то с XMLHttpRequest?
}</pre>
<h2 id="Полифил">Полифил</h2>
<p>Для того, чтобы использовать Fetch в неподдерживаемых браузерах, существует <a href="https://github.com/github/fetch">Fetch Polyfill</a> , который воссоздаёт функциональность для не поддерживающих браузеров.</p>
<h2 id="Спецификации">Спецификации</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Спецификации</th>
<th scope="col">Статус</th>
<th scope="col">Комментарий</th>
</tr>
<tr>
<td>{{SpecName('Fetch')}}</td>
<td>{{Spec2('Fetch')}}</td>
<td>Первоначальное описание</td>
</tr>
</tbody>
</table>
<h2 id="Совместимость_браузера">Совместимость браузера</h2>
<p>{{ CompatibilityTable() }}</p>
<div id="compat-desktop">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Edge</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari (WebKit)</th>
</tr>
<tr>
<td>Базовая поддержка</td>
<td>{{CompatChrome(42)}}</td>
<td>14</td>
<td>{{CompatGeckoDesktop(39)}}<br>
{{CompatGeckoDesktop(34)}}<sup>[1]</sup><br>
{{CompatGeckoDesktop(52)}}<sup>[2]</sup></td>
<td>{{CompatNo}}</td>
<td>29<br>
28<sup>[1]</sup></td>
<td>10.1</td>
</tr>
</tbody>
</table>
</div>
<div id="compat-mobile">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Android Webview</th>
<th>Chrome for Android</th>
<th>Firefox Mobile (Gecko)</th>
<th>IE Phone</th>
<th>Opera Mobile</th>
<th>Safari Mobile</th>
<th>Chrome for Android</th>
</tr>
<tr>
<td>Базовая поддержка</td>
<td>{{CompatChrome(42)}}</td>
<td>{{CompatChrome(42)}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatNo}}</td>
<td>{{CompatUnknown}}</td>
<td>10.1</td>
<td>{{CompatUnknown}}</td>
</tr>
</tbody>
</table>
</div>
<p>[1] Этот API is implemented behind a preference.</p>
<p>[2] До Firefox 52, <code>get()</code> возвращал только первое значение в указанном заголовке, а <code>getAll()</code> возвращал все значения. Начиная с 52, <code>get()</code> теперь возвращает все значения и <code>getAll()</code> был удалён.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li><a href="/en-US/docs/Web/API/ServiceWorker_API">ServiceWorker API</a></li>
<li><a href="/en-US/docs/Web/HTTP/Access_control_CORS">HTTP access control (CORS)</a></li>
<li><a href="/en-US/docs/Web/HTTP">HTTP</a></li>
<li><a href="https://github.com/github/fetch">Fetch polyfill</a></li>
<li><a href="https://github.com/mdn/fetch-examples/">Fetch examples on Github</a></li>
</ul>
|