抓取 API
provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses. It also provides a global
fetch()
method that provides an easy, logical way to fetch resources asynchronously across the network.
This kind of functionality was previously achieved using
XMLHttpRequest
. Fetch provides a better alternative that can be easily used by other technologies such as
服务工作者
. Fetch also provides a single logical place to define other HTTP-related concepts such as CORS and extensions to HTTP.
fetch
规范不同于
jQuery.ajax()
in three main ways:
fetch()
won’t reject on HTTP error status
even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with
ok
status set to false), and it will only reject on network failure or if anything prevented the request from completing.
fetch()
Set-Cookie
headers from other sites are silently ignored.
fetch
won’t send cookies
, unless you set the
credentials
init option
. (Since
Aug 25, 2017
. The spec changed the default credentials policy to
same-origin
. Firefox changed since 61.0b13.)
A basic fetch request is really simple to set up. Have a look at the following code:
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
Here we are fetching a JSON file across the network and printing it to the console. The simplest use of
fetch()
takes one argument — the path to the resource you want to fetch — and returns a promise containing the response (a
响应
对象)。
This is just an HTTP response, not the actual JSON. To extract the JSON body content from the response, we use the
json()
method (defined on the
Body
mixin, which is implemented by both the
Request
and
响应
objects.)
注意 : The Body mixin also has similar methods to extract other types of body content; see the Body section for more.
Fetch requests are controlled by the
connect-src
directive of
Content Security Policy
rather than the directive of the resources it's retrieving.
fetch()
method can optionally accept a second parameter, an
init
object that allows you to control a number of different settings:
见
fetch()
for the full options available, and more details.
// Example POST method implementation:
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, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return 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 `data.json()` call
});
注意,
mode: "no-cors"
only allows a limited set of headers in the request:
Accept
Accept-Language
Content-Language
Content-Type
采用值
application/x-www-form-urlencoded
,
multipart/form-data
,或
text/plain
To cause browsers to send a request with credentials included, even for a cross-origin call, add
credentials: 'include'
到
init
object you pass to the
fetch()
方法。
fetch('https://example.com', {
credentials: 'include'
});
If you only want to send credentials if the request URL is on the same origin as the calling script, add
credentials: 'same-origin'
.
// The calling script is on the origin 'https://example.com'
fetch('https://example.com', {
credentials: 'same-origin'
});
To instead ensure browsers don’t include credentials in the request, use
credentials: 'omit'
.
fetch('https://example.com', {
credentials: 'omit'
})
使用
fetch()
to POST JSON-encoded data.
const data = { username: 'example' };
fetch('https://example.com/profile', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
Files can be uploaded using an HTML
<input type="file" />
input element,
FormData()
and
fetch()
.
const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');
formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);
fetch('https://example.com/profile/avatar', {
method: 'PUT',
body: formData
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
Files can be uploaded using an HTML
<input type="file" multiple />
input element,
FormData()
and
fetch()
.
const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');
formData.append('title', 'My Vegas Vacation');
for (let i = 0; i < photos.files.length; i++) {
formData.append('photos', photos.files[i]);
}
fetch('https://example.com/posts', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
The chunks that are read from a response are not broken neatly at line boundaries and are Uint8Arrays, not strings. If you want to fetch a text file and process it line by line, it is up to you to handle these complications. The following example shows one way to do this by creating a line iterator (for simplicity, it assumes the text is UTF-8, and doesn't handle fetch errors).
async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder('utf-8');
const response = await fetch(fileURL);
const reader = response.body.getReader();
let { value: chunk, done: readerDone } = await reader.read();
chunk = chunk ? utf8Decoder.decode(chunk) : '';
const 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) {
// last line didn't end in a newline char
yield chunk.substr(startIndex);
}
}
async function run() {
for await (let line of makeTextFileLineIterator(urlOfFile)) {
processLine(line);
}
}
run();
A
fetch()
promise will reject with a
TypeError
when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful
fetch()
would include checking that the promise resolved, then checking that the
Response.ok
property has a value of true. The code would look something like this:
fetch('flowers.jpg')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.blob();
})
.then(myBlob => {
myImage.src = URL.createObjectURL(myBlob);
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
Instead of passing a path to the resource you want to request into the
fetch()
call, you can create a request object using the
Request()
constructor, and pass that in as a
fetch()
method argument:
const myHeaders = new Headers();
const myRequest = new Request('flowers.jpg', {
method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default',
});
fetch(myRequest)
.then(response => response.blob())
.then(myBlob => {
myImage.src = URL.createObjectURL(myBlob);
});
Request()
accepts exactly the same parameters as the
fetch()
method. You can even pass in an existing request object to create a copy of it:
const anotherRequest = new Request(myRequest, myInit);
This is pretty useful, as request and response bodies are one use only. Making a copy like this allows you to make use of the request/response again while varying the
init
options if desired. The copy must be made before the body is read, and reading the body in the copy will also mark it as read in the original request.
注意
: There is also a
clone()
method that creates a copy. Both methods of creating a copy will fail if the body of the original request or response has already been read, but reading the body of a cloned response or request will not cause it to be marked as read in the original.
头
interface allows you to create your own headers object via the
Headers()
constructor. A headers object is a simple multi-map of names to values:
const content = 'Hello World';
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Length', content.length.toString());
myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
The same can be achieved by passing an array of arrays or an object literal to the constructor:
const myHeaders = new Headers({
'Content-Type': 'text/plain',
'Content-Length': content.length.toString(),
'X-Custom-Header': 'ProcessThisImmediately'
});
The contents can be queried and retrieved:
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')); // [ ]
Some of these operations are only useful in
ServiceWorkers
, but they provide a much nicer API for manipulating headers.
All of the Headers methods throw a
TypeError
if a header name is used that is not a valid HTTP Header name. The mutation operations will throw a
TypeError
if there is an immutable guard (see below). Otherwise, they fail silently. For example:
const myResponse = Response.error();
try {
myResponse.headers.set('Origin', 'http://mybank.com');
} catch (e) {
console.log('Cannot pretend to be a bank!');
}
A good use case for headers is checking whether the content type is correct before you process it further. For example:
fetch(myRequest)
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new TypeError("Oops, we haven't got JSON!");
}
return response.json();
})
.then(data => {
/* process your data further */
})
.catch(error => console.error(error));
Since headers can be sent in requests and received in responses, and have various limitations about what information can and should be mutable, headers objects have a guard property. This is not exposed to the Web, but it affects which mutation operations are allowed on the headers object.
Possible guard values are:
none
: default.
request
: guard for a headers object obtained from a request (
Request.headers
).
request-no-cors
: guard for a headers object obtained from a request created with
Request.mode
no-cors
.
response
: guard for a Headers obtained from a response (
Response.headers
).
immutable
: Mostly used for ServiceWorkers; renders a headers object read-only.
注意
: You may not append or set a
request
guarded Headers’
Content-Length
header. Similarly, inserting
Set-Cookie
into a response header is not allowed: ServiceWorkers are not allowed to set cookies via synthesized responses.
As you have seen above,
响应
instances are returned when
fetch()
promises are resolved.
The most common response properties you'll use are:
Response.status
— An integer (default value 200) containing the response status code.
Response.statusText
— A string (default value "OK"), which corresponds to the HTTP status code message.
Response.ok
— seen in use above, this is a shorthand for checking that status is in the range 200-299 inclusive. This returns a
布尔
.
They can also be created programmatically via JavaScript, but this is only really useful in
ServiceWorkers
, when you are providing a custom response to a received request using a
respondWith()
方法:
const myBody = new Blob();
addEventListener('fetch', function(event) {
// ServiceWorker intercepting a fetch
event.respondWith(
new Response(myBody, {
headers: { 'Content-Type': 'text/plain' }
})
);
});
Response()
constructor takes two optional arguments — a body for the response, and an init object (similar to the one that
Request()
accepts.)
注意
: The static method
error()
simply returns an error response. Similarly,
redirect()
returns a response resulting in a redirect to a specified URL. These are also only relevant to Service Workers.
Both requests and responses may contain body data. A body is an instance of any of the following types:
ArrayBuffer
ArrayBufferView
(Uint8Array and friends)
Blob
/File
URLSearchParams
FormData
Body
mixin defines the following methods to extract a body (implemented by both
Request
and
响应
). These all return a promise that is eventually resolved with the actual content.
This makes usage of non-textual data much easier than it was with XHR.
Request bodies can be set by passing body parameters:
const form = new FormData(document.getElementById('login-form'));
fetch('/login', {
method: 'POST',
body: form
});
Both request and response (and by extension the
fetch()
function), will try to intelligently determine the content type. A request will also automatically set a
Content-Type
header if none is set in the dictionary.
Fetch API support can be detected by checking for the existence of
头
,
Request
,
响应
or
fetch()
在
Window
or
Worker
scope. For example:
if (window.fetch) {
// run my fetch request here
} else {
// do something with XMLHttpRequest?
}
To use Fetch in unsupported browsers, there is a Fetch Polyfill available that recreates the functionality for non-supporting browsers.
| 规范 | 状态 | 注释 |
|---|---|---|
| Fetch | 实时标准 | 初始定义 |
| 桌面 | 移动 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
fetch
|
Chrome 42 | Edge 14 |
Firefox
39
|
IE No |
Opera
29
|
Safari 10.1 | WebView Android 42 | Chrome Android 42 |
Firefox Android
39
|
Opera Android
29
|
Safari iOS 10.3 | Samsung Internet Android 4.0 |
| Support for blob: and data: | Chrome 48 | Edge 79 | Firefox ? | IE No | Opera ? | Safari ? | WebView Android 43 | Chrome Android 48 | Firefox Android ? | Opera Android ? | Safari iOS ? | Samsung Internet Android 5.0 |
| referrerPolicy | Chrome 52 | Edge 79 | Firefox 52 | IE No | Opera 39 | Safari 11.1 | WebView Android 52 | Chrome Android 52 | Firefox Android 52 | Opera Android 41 | Safari iOS No | Samsung Internet Android 6.0 |
| signal | Chrome 66 | Edge 16 | Firefox 57 | IE No | Opera 53 | Safari 11.1 | WebView Android 66 | Chrome Android 66 | Firefox Android 57 | Opera Android 47 | Safari iOS 11.3 | Samsung Internet Android 9.0 |
| Streaming response body | Chrome 43 | Edge 14 |
Firefox
Yes
Disabled
|
IE No | Opera 29 | Safari 10.1 | WebView Android 43 | Chrome Android 43 | Firefox Android No | Opera Android No | Safari iOS 10.3 | Samsung Internet Android 4.0 |
完整支持
不支持
兼容性未知
实验。期望将来行为有所改变。
见实现注意事项。
用户必须明确启用此特征。