likes
comments
collection
share

Fetch API快速入门及参数详解

作者站长头像
站长
· 阅读数 32

写在前面: Fetch API是一种基于Promise、由Web原生提供的、更现代的实现Ajax的技术。本文对Fetch API的使用进行了简单介绍,同时更注重于与Fethc API相关的接口的参数解析。

从一个简单的fetch开始

fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
  console.log('response', response);
  return response.json();
})
.then((json) => {
  console.log('JSON', json);
});

简单看出fetch有两个特点:

  • fetch的返回值是一个Promise,请求的Response在这个Promise
  • 需要经过两个Promise才能拿到真正的服务器返回的数据

fetch()方法

fetch()方法接受两个参数:

  • resource - 经常是一个URL字符串,也可以是一个 Request 对象
  • init options - 如果第一个参数是URL字符串,可以借助该参数配置一些请求参数

fetch()方法除了可以直接传参数调用,也可以直接传一个Request对象。二者是等价的。

init options参数

const res = fetch('url', init);

const init = {
  method: 'POST', // *GET, POST, PUT, DELETE...
  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'
    // 'Content-Type': 'text/plain'
    // ...
  },
  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
  signal: signal,  // AbortSignal object instance
};
  • method : HTTP请求方法,默认使用 GET
  • mode : 指定该请求是
    • 跨域( cors ),默认值
    • 仅允许同源( same-origin ),否则抛出TypeError
      • no-cors :
        • 仅允许 HEAD , GETPOST 方法
        • 仅可以设置simple headers
        • 可以发出请求但是不能拿到Response
  • cache : 指定该请求的HTTP缓存规则,详情解释可以参考MDN Request.cache,但是还是推荐通过HTTP header设置 Cache-Control来控制客户端使用缓存规则。
    • default : 默认行为,fresh使用cache,stale与服务器通信检查资源变化(协商缓存)
    • no-cache : 无论fresh还是stale,都与服务器通信检查资源变化(协商缓存)
    • force-cache : 无论fresh还是stale,都使用cache
  • credentials : 指定该请求是否携带cookie或其他credentials
    • include : 总是携带cookie,无论同源还是跨域
    • same-origin : 默认值,仅在同源请求时携带cookie
    • omit : 从不携带cookie
  • headers : 接受一个string object或者Headers对象
  • referrer : 标识请求的“来源”,一般不建议手动设置
    • same-origin URL
    • about:client
    • empty string “”
  • referrerpolicy : 指定设置 Referer HTTP header的规则
    • 默认值strict-origin-when-cross-origin ,具体行为表现为:
      • 同源请求,Referer header中写入完整的URL
      • 跨域请求,Referer header中仅写入当前请求URL的origin
      • 请求non-potentially trustworthy URL,无Referer header
    • 其他参数的具体行为参考W3C Spec Referrer Policies
  • redirect : 指定遇到重定向的行为
    • follow : 默认值,跟随重定向URL
    • error : 如果遇到重定向,中断请求抛出Error
    • manual
  • body : 请求体,数据类型必须和 Content-Type header 相匹配
  • signal : 一个 AbortSignal 对象实例,用于和 AbortController 通信中断请求

返回值 - Promise<Response>

Promise

  • fetch()的返回值是一个Promise

  • 不会reject HTTP 异常状态(HTTP code ≥ 300)

    • 异常状态的 Response.ok 为false,并且 Response.status 会给出具体的状态码。可以依据以上两个字段去做异常处理。

    • 因此HTTP 异常状态不会被catch到,需要手动抛出Error才会被catch到

      fetch('https://jsonplaceholder.typicode.com/posts/999')
        .then((response) => {
          console.log('response', response);
          if (!response.ok) {
            // 判断ok属性为false,然后手动抛出Error
            throw new Error(`status code: ${response.status}`);
          }
        })
        .catch((err) => {
          console.log({ err });
          // err: Error: status code: 404
        });
      
  • 只有遇到阻止request发出的Error才会reject

    • client side network failure 或者 request init options不规范会抛出TypeError

      fetch('https://jsonplaceholder.typicode.com/posts/999', {
        method: 'GET',
        body: new FormData(),  // GET方法不能带body,因此request会被阻止,抛出TypeError
      })
        .then((response) => {
          console.log('response', response);
        })
        .catch((err) => {
          console.log({ err });
          // err: TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
        });
      
    • 通过 AbortController 中止一个请求的时候会抛出 AbortError

    • 具体可以参考MDN给出的Exceptions发生的原因

Response

  • Response对象是对返回结果的一层封装
  • 可以通过 ok , status , statusText 等字段获取返回结果的状态
  • 真正从server返回的数据在 body 里,但是被封装成了 ReadableStream ,需要调用原型链上的方法解析

Fetch接口

Headers

Fetch API快速入门及参数详解

  • Headers对象有多种创建方式

    // ✅ string object, 显示地指定key为string
    new Headers({ 
      'Content-type': 'application/json; charset=UTF-8'
    });
    
    // ❌ 直接写成camelCased会多出一个无法识别的header,并且真正的content-type会被默认指定为‘text/plain’
    new Headers({ 
      ContentType: 'application/json; charset=UTF-8'
    });
    
    // 也可以通过二维数组创建
    const headers = [
      ['Set-Cookie', 'greeting=hello'],
      ['Set-Cookie', 'name=world']
    ];
    const myHeaders = new Headers(headers);
    

Fetch API快速入门及参数详解

  • Headers类似于Map,也具有 get() , set() , entries() 等方法
  • Headers具有Guard机制,即在特定类型的请求下,限制一些header names不能出现在Headers

Response

Fetch API快速入门及参数详解

  • ok : 标记这个请求是否成功(HTTP status code 属于200-209)
  • status and statusText :
    • 分别返回HTTP status code和其对应的status message,具体可以参考HTTP response status codes
    • HTTP/2 不支持status message,会返回空字符串
  • headers : 返回一个Headers对象
  • body and bodyUsed :
    • body 的返回类型是一个 ReadableStream ,需要调用它的reader去读取stream里面的数据

      /* Response.body 手动处理ReadableStream */
      fetch('https://jsonplaceholder.typicode.com/posts/1')
        .then((response) => {
          console.log('response', response);
          return response.body;
        })
        .then((body) => {
          const reader = body.getReader();
          return reader.read();
        })
        .then((data) => {
          const { value } = data;
          const utf8Decoder = new TextDecoder('utf-8');
          console.log('raw', data);
          console.log('parsed', JSON.parse(utf8Decoder.decode(data.value)));
        })
        .catch((err) => console.log(err));
      
    • 实际上不需要这么麻烦,Response原型链上提供了若干封装好的reader,调用它们可以直接返回解析对应数据的Promise

      • json() , blob() , formData() , text() , arrayBuffer()

        /* parse image file by Response.blob() */
        const myImage = document.querySelector('img');
        
        const myRequest = new Request('flowers.jpg');
        
        fetch(myRequest)
          .then((response) => { 
        
            console.log('bodyUsedBefore', response.bodyUsed); // false
            const promise = response.blob();
            console.log('bodyUsedAfter', response.bodyUsed); // true
        
            return promise
          })
          .then((myBlob) => {
            const objectURL = URL.createObjectURL(myBlob);
            myImage.src = objectURL;
          });
        
      • ReadableStream 的处理是Fetch API的另一个话题,感兴趣可以阅读这篇来自网易云音乐技术团队的博客从 Fetch 到 Streams —— 以流的角度处理网络请求

    • bodyUsed 用于标记这个 Response.body 的stream是否被读取过

  • redirected :
    • 用于标记这个Response是否来自于重定向
    • 但是MDN不建议通过该字段来过滤重定向,因为不够安全
    • 更推荐的做法是在发起请求时配置init options: fetch(’url’, { redirect: 'error' });

Request

fetch()方法也接受一个Request对象作为参数。Request对象的构造器需要的参数和fetch()方法相同。

const initOptions = {
  method: 'POST',
  body: JSON.stringify({
    title: 'Test',
    body: 'Fetch API test',
    userId: 233,
  }),
  headers: {
    'Content-type': 'application/json; charset=UTF-8',
  },
  mode: 'same-origin',
  credentials: 'omit',
  referrerPolicy: 'no-referrer',
  redirect: 'error',
};

// 常见调用
fetch('https://jsonplaceholder.typicode.com/posts/1', initOptions);

// 使用Request对象
const request = new Request('https://jsonplaceholder.typicode.com/posts/1', initOptions);
fetch(request);

console.log(request);

Fetch API快速入门及参数详解

中断请求

如果需要中止fetch请求,可以使用 AbortController 接口。 AbortController 具体使用分为四步:

  1. 实例化一个 AbortController 对象
  2. AbortController 对象的 signal 属性上获取到一个 AbortSignal 对象
  3. 在fetch方法的 init options 配置里携带这个 AbortSignal 对象
  4. 使用 AbortController 对象的 abort() 方法中止一个fetch请求

⚠️ abort() 不是中止了一次fetch请求,而至永久中止了携带该 signal 的请求。如果仅需要中止一次,需要在下次请求的时候重新实例化一个 AbortController 对象,重新携带新的 signal

/* Abort a request */

// 获取AbortController对象
const abortController = new AbortController();
// 获取AbortSignal对象
const { signal } = abortController;

// 在配置中携带该signal
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
  .then((res) => {
    console.log('res', res);
  })
  .catch((err) => {
    // 会捕捉到一个AbortError
    console.log('1st abort error:\n', err);
  });

// 请求发出后立刻中止请求
abortController.abort();

// 携带已经被被abort的signal再次发送请求会失败
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    // 请求失败,捕捉到异常
    console.log('2st abort error:\n', err);
  });

// 重新获取一个signal才能成功发送请求
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  signal: new AbortController().signal,
})
  .then((res) => {
    console.log('new res', res);
  })
  .catch((err) => {
    // 请求成功,没有被捕捉到异常
    console.log('3st abort error:\n', err);
  });

Fetch API快速入门及参数详解

其他值得一读的文章

转载自:https://juejin.cn/post/7133975279751413768
评论
请登录