# Response API

网道（WangDoc.com），互联网文档计划

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

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

## 构造方法 #

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

    // 定义
    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`是一个对象，代表服务器返回的数据头，类型描述如下。

    {
      status: Number
      statusText: String
      headers: Object
    }

下面是一个例子。

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

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

    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 对象。

    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 对象。

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

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

### ok #

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

    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等），该属性只读。

    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`属性是一个数值，代表服务器返回的状态码，该属性只读。

    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()`方法，其他几个方法的用法都大同小异。

    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()`方法。

    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()`方法。

    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 对象。

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

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

## 静态方法 #

### Response.json() #

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

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

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

下面是示例。

    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，表示拒绝发送。

    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，表示跳转到其他网址。

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

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

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

本教程采用知识共享 署名-相同方式共享 3.0协议。

分享本文