fetch知识点汇总

使用XHR发送一个json请求一般是这样:

 1 const xhr = new XMLHttpRequest()
 2 xhr.open('Get', url)
 3 xhr.responseType = 'json'
 4 
 5 xhr.onload = () => {
 6     console.log(xhr.response)      
 7 }
 8 
 9 xhr.onerror = () => {
10     console.log('error')
11 }
12 
13 xhr.send()

 

使用fetch的实例:

1 fetch(url).then(response => response.json())
2     .then(data => console.log(data))
3     .catch(e => console.log('error', e))

 

Fetch参数

fetch(input [,init])

 input(必须) 定义要获取的资源(请求地址)

 init(可选)

 

参数 | 描述

method   请求使用的方法,如GET、POST

headers   http请求头(user-Agent, Cookie)

body   请求的body信息

mode

fetch可以设置不同的模式使得请求有效. 模式可在fetch方法的第二个参数对象中定义.

可定义的模式如下:

  •  same-origin: 表示同域下可请求成功; 反之, 浏览器将拒绝发送本次fetch, 同时抛出错误 “TypeError: Failed to fetch(…)”.
  •  cors: 表示同域和带有CORS响应头的跨域下可请求成功. 其他请求将被拒绝.
  • cors-with-forced-preflight: 表示在发出请求前, 将执行preflight检查.
  • no-cors: 常用于跨域请求不带CORS响应头场景, 此时响应类型为 “opaque”.

除此之外, 还有两种不太常用的mode类型, 分别是 navigate , websocket , 它们是 HTML标准 中特殊的值, 这里不做详细介绍.

 credentials

 omit(缺省值,默认为该值)、same-origin(同源,便是同域请求才发送cookie)、include(任何请求都带cookie)

cache

  • default(表示fetch请求之前将检查下http的缓存)
  • no-store(表示fetch请求将完全忽略http缓存的存在,这意味着请求之前将不再检查下http的缓存, 拿到响应后, 它也不会更新http缓存)
  • no-cache(如果存在缓存,那么fetch将发送一个条件查询request和一个正常的request, 拿到响应后,它会更新http缓存)
  • reload(表示fetch请求之前将忽略http缓存的存在, 但是请求拿到响应后,它将主动更新http缓存)
  • force-cache(表示fetch请求不顾一切的依赖缓存, 即使缓存过期了,它依然从缓存中读取. 除非没有任何缓存, 那么它将发送一个正常的request)
  • only-if-cached(表示fetch请求不顾一切的依赖缓存, 即使缓存过期了,它依然从缓存中读取. 如果没有缓存, 它将抛出网络错误(该设置只在mode为”same-origin”时有效).

如果fetch请求的header里包含 If-Modified-Since, If-None-Match, If-Unmodified-Since, If-Match, 或者 If-Range 之一, 且cache的值为 default , 那么fetch将自动把 cache的值设置为 "no-store"

 

 Fetch - response type

 一个fetch请求的相应类型(response.type)为如下三种之一:

  • baisc (同域下,相应类型为basic)
  • cors (同样是跨域下, 如果服务器返回了CORS响应头, 那么响应类型将为 “cors”. 此时响应头中除 Cache-Control , Content-Language , Content-Type , Expores , Last-Modified 和 Progma 之外的字段都不可见.)
  • opaque ( 跨域下, 服务器没有返回CORS响应头, 响应类型为 “opaque”. 此时我们几乎不能查看任何有价值的信息, 比如不能查看response, status, url等等等等.)

注意: 无论是同域还是跨域, 以上 fetch 请求都到达了服务器.

 

 Fetch 常见坑

  1.Fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: 'include'})

    默认情况下, fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于维护一个用户会话,则导致未经认证的请求(要发送 cookies,必须发送凭据头)

  2.服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。

    当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 reolve 的返回值的 ok 属性设置为 false, 想要精确判断fetch()是否成功,需要包含 promise resolved 的情况,此时再判断 Response.ok 是不是为 true。HTTP状态码为200-299是才会设置为true), 仅当网络故障时或请求被阻止时,才会标记为 rejec

 

使用Fetch封装request方法

http.js

  1 import 'whatwg-fetch';
  2 
  3 const netErrorStatu = 1;    // 网络错误
  4 const serverErrorStatu = 2;    // 服务器错误
  5 const formatErrorStatu = 3;    // 数据格式错误
  6 const logicErrorStatu = 4;    // 业务逻辑错误
  7 
  8 const errorMsg = {
  9     [netErrorStatu]: '网络错误',
 10     [serverErrorStatu]: '服务器错误',
 11     [formatErrorStatu]: '数据格式错误',
 12     [logicErrorStatu]: '业务逻辑错误'
 13 };
 14 
 15 class CustomFetchError {
 16     constructor(errno, data) {
 17         this.errno = errno;
 18         this.msg = errorMsg[errno];
 19         this.data = data;
 20     }
 21 }
 22 
 23 export function buildQuery(data) {
 24     const toString = Object.prototype.toString;
 25 
 26     const res = Object.entries(data).reduce((pre, [key, value]) => {
 27         let newValue;
 28 
 29         if (Array.isArray(value) || toString.call(value) === '[object Object]') {
 30             newValue = JSON.stringify(value);
 31         } else {
 32             newValue = (value === null || value === undefined) ? '' : value;
 33         }
 34 
 35         pre.push(`${key}=${encodeURIComponent(newValue)}`);
 36 
 37         return pre;
 38     }, []);
 39 
 40     return res.join('&');
 41 }
 42 
 43 export async function request(input, opt) {
 44     // 设置请求默认带cookie
 45     const init = Object.assign({
 46         credentials: 'include'
 47     }, opt);
 48 
 49     let res;
 50     // 仅当网络故障时或请求被阻止时,才会标记为 rejec
 51     try {
 52         res = await fetch(input, init);
 53     } catch (e) {
 54         throw new CustomFetchError(netErrorStatu, e);
 55     }
 56     // 仅HTTP状态码为200-299是才会设置为true
 57     if (!res.ok) {
 58         throw new CustomFetchError(serverErrorStatu, res);
 59     }
 60 
 61     let data;
 62     // fetch()请求返回的response是Stream对象,调用response.json时由于异步读取流对象所以返回的是一个Promise对象    
 63     try {
 64         data = await res.json();
 65     } catch (e) {
 66         throw new CustomFetchError(formatErrorStatu, e);
 67     }
 68     // 根据和后台的约定设置的错误处理
 69     if (!data || data.errno !== 0) {
 70         throw new CustomFetchError(logicErrorStatu, data);
 71     }
 72 
 73     return data.data;
 74 }
 75 
 76 export function get(url, params = {}, opt = {}) {
 77     const init = Object.assign({
 78         method: 'GET'
 79     }, opt);
 80 
 81     // ajax cache
 82     Object.assign(params, {
 83         timestamp: new Date().getTime()
 84     });
 85 
 86     const paramsStr = buildQuery(params);
 87 
 88     const urlWithQuery = url + (paramsStr ? `?${paramsStr}` : '');
 89 
 90     return request(urlWithQuery, init);
 91 }
 92 
 93 export function post(url, params = {}, opt = {}) {
 94     const headers = {
 95         'Content-Type': 'application/x-www-form-urlencoded'
 96     };
 97 
 98     const init = Object.assign({
 99         method: 'POST',
100         body: buildQuery(params),
101         headers
102     }, opt);
103 
104     return request(url, init);
105 }
106 
107 export default {
108     request,
109     get,
110     post
111 };

 

requset.js

 1 import { notification } from 'antd';
 2 import Loading from 'components/Loading';
 3 import { LOGIN_URL } from 'constants/basic';
 4 import * as http from './http';
 5 
 6 const loading = Loading.newInstance();
 7 
 8 async function request(method, url, params, opt = {}, httpOpt) {
 9     /**
10      * needLoading 是否添加loading图片
11      * checkAccount 验证未登陆是否跳登陆页面
12      * showErrorMsg 是都显示通用错误提示
13      */
14     const {
15         needLoading = true,
16         checkAccount = true,
17         showErrorMsg = true
18     } = opt;
19 
20     if (needLoading) {
21         loading.add();
22     }
23 
24     let res;
25 
26     try {
27         res = await http[method](url, params, httpOpt);
28     } catch (e) {
29         if (checkAccount && e.errno === 4 && e.data.errno === 10000) {
30             location.href = LOGIN_URL;
31         }
32 
33         if (showErrorMsg) {
34             notification.error({
35                 message: '提示信息',
36                 description: e.errno === 4 ? e.data.msg : e.msg
37             });
38         }
39 
40         throw e;
41     } finally {
42         if (needLoading) {
43             loading.remove();
44         }
45     }
46 
47     return res;
48 }
49 
50 export function get(...arg) {
51     return request('get', ...arg);
52 }
53 
54 export function post(...arg) {
55     return request('post', ...arg);
56 }

 

  github的代码地址: https://github.com/haozhaohang/library

posted @ 2017-12-02 12:33  怜白  阅读(3285)  评论(0编辑  收藏  举报