顾名思义,Request 对象是获取资源请求的接口。这个接口暴露了请求的相关信息,也暴露了使
用请求体的不同方式。
注意 与请求体相关的属性和方法将在。
1. 创建 Request 对象
可以通过构造函数初始化 Request 对象。为此需要传入一个 input 参数,一般是 URL:
let r = new Request('https://foo.com');
console.log(r);
// Request {...}
Request 构造函数也接收第二个参数——一个 init 对象。这个 init 对象与前面介绍的 fetch() 的 init 对象一样。没有在 init 对象中涉及的值则会使用默认值:
// 用所有默认值创建 Request 对象 console.log(new Request(''));
// Request {
// bodyUsed: false
// cache: "default"
// credentials: "same-origin"
// destination: ""
// headers: Headers {}
Fetch API 提供了两种不太一样的方式用于创建 Request 对象的副本:使用 Request 构造函数和使 用 clone()方法。
将 Request 实例作为 input 参数传给 Request 构造函数,会得到该请求的一个副本:
let r1 = new
Request('https://foo.com');
let r2 = new Request(r1); console.log(r2.url); // https://foo.com/
如果再传入 init 对象,则 init 对象的值会覆盖源对象中同名的值:
let r1 = new Request('https://foo.com');
let r2 = new Request(r1, {method: 'POST'}); console.log(r1.method); // GET
console.log(r2.method); // POST
这种克隆方式并不总能得到一模一样的副本。最明显的是,第一个请求的请求体会被标记为“已使用”:
// integrity: ""
// keepalive: false
// method: "GET"
// mode: "cors"
// redirect: "follow"
// referrer: "about:client"
// referrerPolicy: ""
// signal: AbortSignal {aborted: false, onabort: null}
// url: "<current URL>"
// }
// 用指定的初始值创建 Request 对象 console.log(new Request('https://foo.com',
{ method: 'POST' }));
// Request {
// bodyUsed: false
// cache: "default"
// credentials: "same-origin"
// destination: ""
// headers: Headers {}
// integrity: ""
// keepalive: false
// method: "POST"
// mode: "cors"
// redirect: "follow"
// referrer: "about:client"
// referrerPolicy: ""
// signal: AbortSignal {aborted: false, onabort: null}
// url: "https://foo.com/" // }
2. 克隆 Request 对象
let r1 = new Request('https://foo.com',
{ method: 'POST', body: 'foobar' });
console.log(r2.bodyUsed); // false 28
let r2 = new Request(r1);
console.log(r1.bodyUsed); // true
如果源对象与创建的新对象不同源,则 referrer 属性会被清除。此外,如果源对象的 mode 为 navigate,则会被转换为 same-origin。
第二种克隆 Request 对象的方式是使用 clone()方法,这个方法会创建一模一样的副本,任何值 都不会被覆盖。与第一种方式不同,这种方法不会将任何请求的请求体标记为“已使用”:
let r1 = new Request('https://foo.com', { method: 'POST', body: 'foobar' }); let r2 = r1.clone();
console.log(r1.url); // https://foo.com/
console.log(r2.url); // https://foo.com/
console.log(r1.bodyUsed); // false
console.log(r2.bodyUsed); // false
如果请求对象的 bodyUsed 属性为 true(即请求体已被读取),那么上述任何一种方式都不能用来 创建这个对象的副本。在请求体被读取之后再克隆会导致抛出 TypeError。
let r = new Request('https://foo.com'); r.clone();
new Request(r);
// 没有错误
r.text(); // 设置bodyUsed为true
r.clone();
// TypeError: Failed to execute 'clone' on 'Request': Request body is already used
new Request(r);
// TypeError: Failed to construct 'Request': Cannot construct a Request with a Request object that has already been used.
3. 在 fetch()中使用 Request 对象
fetch()和 Request 构造函数拥有相同的函数签名并不是巧合。在调用 fetch()时,可以传入已
经创建好的 Request 实例而不是 URL。与 Request 构造函数一样,传给 fetch()的 init 对象会覆 盖传入请求对象的值:
let r = new Request('https://foo.com');
// 向 foo.com 发送 GET 请求 fetch(r);
// 向 foo.com 发送 POST 请求 fetch(r, { method: 'POST' });
fetch()会在内部克隆传入的 Request 对象。与克隆 Request 一样,fetch()也不能拿请求体已 经用过的 Request 对象来发送请求:
let r = new Request('https://foo.com',
{ method: 'POST', body: 'foobar' });
r.text();
fetch(r);
// TypeError: Cannot construct a Request with a Request object that has already been used.
关键在于,通过 fetch 使用 Request 会将请求体标记为已使用。也就是说,有请求体的 Request 只能在一次 fetch 中使用。(不包含请求体的请求不受此限制。)演示如下:
let r = new Request('https://foo.com',
{ method: 'POST', body: 'foobar' });
fetch(r);
fetch(r);
// TypeError: Cannot construct a Request with a Request object that has already been used.
要想基于包含请求体的相同 Request 对象多次调用 fetch(),必须在第一次发送 fetch()请求前 调用 clone():
let r = new Request('https://foo.com',
{ method: 'POST', body: 'foobar' });
// 3 个都会成功 19 fetch(r.clone());
fetch(r.clone());
fetch(r);