# Response API

浏览器原生提供`Response()`构造函数，用来构造服务器响应。

`fetch()`方法返回的就是一个 Response 对象。

## 构造方法

`Response()`作为构造方法调用时，返回 Response 实例。

```javascript
// 定义
new Response([body:Object, [init : Object]]): Response

// 用法
new Response()
new Response(body)
new Response(body, options)
```

它带有两个参数，都是可选的。

第一个参数`body`代表服务器返回的数据体，必须是下面类型之一：ArrayBuffer、ArrayBufferView、Blob、FormData、ReadableStream、String、URLSearchParams。

第二个参数`init`是一个对象，代表服务器返回的数据头，类型描述如下。

```javascript
{
  status: Number
  statusText: String
  headers: Object
}
```

下面是一个例子。

```javascript
const myBlob = new Blob();
const myOptions = { status: 200, statusText: "OK" };
const myResponse = new Response(myBlob, myOptions);
```

注意，如果返回 JSON 数据，必须将其转成字符串返回。

```javascript
const data = {
  hello: "world",
};

const json = JSON.stringify(data, null, 2);

const result = new Response(json, {
  headers: {
    "content-type": "application/json;charset=UTF-8",
  },
});
```

上面示例中，构造一个返回 JSON 数据的 Response 对象，就必须用`JSON.stringify()`方法，将第一个参数转为字符串。

## 实例属性

### body，bodyUsed

`body`属性代表数据体，是一个只读的 ReadableStream 对象。

```javascript
const res = await fetch('/fireworks.ogv');
const reader = res.body.getReader();

let result;
while (!(result = await reader.read()).done) {
  console.log('chunk size:', result.value.byteLength);
}
```

上面示例中，先建立一个 body 的读取器，然后每次读取一段数据，输出这段数据的字段长度。

注意，`body`是一个 Stream 对象，只能读取一次。取出所有数据以后，第二次就读不到了。

`bodyUsed`属性是一个只读的布尔值，表示`body`属性是否已经读取。

### headers

`headers`属性代表服务器返回的数据头，是一个只读的 Headers 对象。

```javascript
const res = await fetch('/flowers.jpg');
console.log(...res.headers);
```

上面示例中，发出请求后，展开打印`res.headers`属性，即服务器回应的所有消息头。

### ok

`ok`属性是一个布尔值，表示服务器返回的状态码是否成功（200到299），该属性只读。

```javascript
const res1 = await fetch('https://httpbin.org/status/200');
console.log(res1.ok); // true

const res2 = await fetch('https://httpbin.org/status/404');
console.log(res2.ok); // false
```

### redirected

`redirected`是一个布尔值，表示服务器返回的状态码是否跳转类型（301，302等），该属性只读。

```javascript
const res1 = await fetch('https://httpbin.org/status/200');
console.log(res1.redirected); // false

const res2 = await fetch('https://httpbin.org/status/301');
console.log(res2.redirected); // true
```

### status，statusText

`status`属性是一个数值，代表服务器返回的状态码，该属性只读。

```javascript
const res1 = await fetch('https://httpbin.org/status/200');
console.log(res1.status); // 200

const res2 = await fetch('https://httpbin.org/status/404');
console.log(res2.status); // 404
```

`statusText`属性是一个字符串，代表服务器返回的状态码的文字描述。比如，状态码200的`statusText`一般是`OK`，也可能为空。

### type

`type`属性是一个只读字符串，表示服务器回应的类型，它的值有下面几种：basic、cors、default、error、opaque、opaqueredirect。

### url

`url`属性是一个字符串，代表服务器路径，该属性只读。如果请求是重定向的，该属性就是重定向后的 URL。

## 实例方法

### 数据读取

以下方法可以获取服务器响应的消息体，根据返回数据的不同类型，调用相应方法。

- .json()：返回一个 Promise 对象，最终得到一个解析后的 JSON 对象。
- .text()：返回一个 Promise 对象，最终得到一个字符串。
- .blob()：返回一个 Promise 对象，最终得到一个二进制 Blob 对象，代表某个文件整体的原始数据。
- .arrayBuffer()：返回一个 Promise 对象，最终得到一个 ArrayBuffer 对象，代表一段固定长度的二进制数据。
- .formData()：返回一个 Promise 对象，最终得到一个 FormData 对象，里面是键值对形式的表单提交数据。

下面是从服务器获取 JSON 数据的一个例子，使用`.json()`方法，其他几个方法的用法都大同小异。

```javascript
async function getRedditPosts() {
  try {
    const response = await fetch('https://www.reddit.com/r/all/top.json?limit=10');
    const data = await response.json();
    const posts = data.data.children.map(child => child.data);
    console.log(posts.map(post => post.title));
  } catch (error) {
    console.error(error);
  }
}
```

下面是从服务器获取二进制文件的例子，使用`.blob()`方法。

```javascript
async function displayImageAsync() {
  try {
    const response = await fetch('https://www.example.com/image.jpg');
    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    const img = document.createElement('img');
    img.src = url;
    document.body.appendChild(img);
  } catch (error) {
    console.error(error);
  }
}
```

下面是从服务器获取音频文件，直接解压播放的例子，使用`.arrayBuffer()`方法。

```javascript
async function playAudioAsync() {
  try {
    const response = await fetch('https://www.example.com/audio.mp3');
    const arrayBuffer = await response.arrayBuffer();
    const audioBuffer = await new AudioContext().decodeAudioData(arrayBuffer);
    const source = new AudioBufferSourceNode(new AudioContext(), { buffer: audioBuffer });
    source.connect(new AudioContext().destination);
    source.start(0);
  } catch (error) {
    console.error(error);
  }
}
```

### clone()

`clone()`方法用来复制 Response 对象。

```javascript
const res1 = await fetch('/flowers.jpg');
const res2 = res1.clone();
```

复制以后，读取一个对象的数据，不会影响到另一个对象。

## 静态方法

### Response.json()

`Response.json()`返回一个 Response 实例，该实例对象的数据体就是作为参数的 JSON 数据，数据头的`Content-Type`字段自动设为`application/json`。

```javascript
Response.json(data)
Response.json(data, options)
```

`Response.json()`基本上就是`Response()`构造函数的变体。

下面是示例。

```javascript
const jsonResponse1 = Response.json({ my: "data" });

const jsonResponse2 = Response.json(
  { some: "data", more: "information" },
  { status: 307, statusText: "Temporary Redirect" },
);
```

### Response.error()

`Response.error()`用来构造一个表示报错的服务器回应，主要用在 Service worker，表示拒绝发送。

```javascript
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);
  if (url.pathname === '/flowers.jpg') {
    event.respondWith(Response.error());
  }
});
```

### Response.redirect()

`Response.redirect()`用来构造一个表示跳转的服务器回应，主要用在 Service worker，表示跳转到其他网址。

```javascript
Response.redirect(url)
Response.redirect(url, status)
```

这个方法的第一个参数`url`是所要跳转的目标网址，第二个参数是状态码，一般是301或302（默认值）。

```javascript
Response.redirect("https://www.example.com", 302);
```