1
头图

Fetch

Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。

它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

这种功能以前是使用 XMLHttpRequest 实现的。

Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如 Service Workers。

Fetch 还提供了专门的逻辑空间来定义其他与 HTTP 相关的概念,例如 CORS 和 HTTP 的扩展。

请注意,fetch 规范jQuery.ajax() 主要有以下的不同:

  • 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve(如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false),仅当网络故障时或请求被阻止时,才会标记为 reject
  • fetch 不会发送跨域 cookie,除非你使用了 credentials 的初始化选项

一个基本的 fetch 请求设置起来很简单。看看下面的代码:

fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));

参考 fetch(),查看所有可选的配置和更多描述。

// Example POST method implementation:
async function postData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    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',
    },
    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
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://example.com/answer', { answer: 42 })
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

注意:mode: "no-cors" 仅允许使用一组有限的 HTTP 请求头:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type 允许使用的值为:application/x-www-form-urlencodedmultipart/form-datatext/plain

MDN地址: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Us...

qs简介

一个带有一些附加安全性查询字符串解析字符串化库。

主要维护者:Jordan Harband

最初创建者和维护者:TJ Holowaychuk

npmjs仓库地址: https://www.npmjs.com/package/qs

react 使用qs

安装

npm install qs

import qs from'qs'

qu.stringify()和qs.parse()

  • qu.stringify() :将对象序列化成url的形式;以&进行拼接
  • qs.parse():将url解析成对象形式;

1、qs.parse() 将URL解析成对象的形式

import  Qs from 'qs';
let url = 'method=query_sql_dataset_d<a style="color:transparent">来源gao*daima.com搞@代#码网</a>ata&projectId=85&appToken=7d22e38e-5717-11e7-907b-a6006ad3dba0';
Qs.parse(url);
console.log(Qs.parse(url));

输出结果

{
    method:'query_sql_dataset_data',
    projectId:'85',
    appToken:'7d22e38e-5717-11e7-907b-a6006ad3dba0'
}

2、qs.stringify()将对象序列化成URL的形式,以&进行拼接(可用于发送查询条件)

import  Qs from 'qs';
let obj= {
     method: "query_sql_dataset_data",
     projectId: "85",
     appToken: "7d22e38e-5717-11e7-907b-a6006ad3dba0",
     datasetId: " 12564701"
   };
Qs.stringify(obj);
console.log(Qs.stringify(obj));

输出的是:

method=query_sql_dataset_data&projectId=85&appToken=7d22e38e-5717-11e7-907b-a6006ad3dba0&datasetId=%12564701

JSON.stringify(param) VS Qs.stringify(param)

JSON中同样存在stringify方法,但是和Qs.stringify之间的区别是很明显的

// JSON.stringify(param) 
{"uid":"cs11","pwd":"000000als","username":"cs11","password":"000000als"}

// Qs.stringify(param)
uid=cs11&pwd=000000als&username=cs11&password=000000als

封装fetch

为什么封装Fetch请求

  • 请求的地方代码更少。
  • 公共的错误统一一个地方添加即可。
  • 请求定制的错误还是请求自己也可以处理。
  • 扩展性好,添加功能只需要改一个地方。

总结起来一句话:简单,好用

封装Fetch请求(主要文件)

"./fetchUtil"

import qs from 'qs';
import { message } from 'antd';

const { stringify, parse } = qs;

const checkStatus = res => {
  if (200 >= res.status < 300) {
    return res;
  }
  message.error(`网络请求失败,${res.status}`);
  const error = new Error(res.statusText);
  error.response = error;
  throw error;
};

/**
 *  捕获成功登录过期状态码等
 * @param res
 * @returns {*}
 */
const judgeOkState = async res => {
  const cloneRes = await res.clone().json(); 
  
  //TODO:可以在这里管控全局请求
  if (!!cloneRes.code && cloneRes.code !== 200) {
    message.error(`11${cloneRes.msg}${cloneRes.code}`);
  }
  return res;
};

/**
 * 捕获失败
 * @param error
 */
const handleError = error => {
  if (error instanceof TypeError) {
    message.error(`网络请求失败啦!${error}`);
  }
  return {   //防止页面崩溃,因为每个接口都有判断res.code以及data
    code: -1,
    data: false,
  };
};

class http {
  /**
   *静态的fetch请求通用方法
   * @param url
   * @param options
   * @returns {Promise<unknown>}
   */
  static async staticFetch(url = '', options = {}) {

    const defaultOptions = {
      /*允许携带cookies*/
      credentials: 'include',
      /*允许跨域**/
      mode: 'cors',
      headers: {
        token: null,
        Authorization: null,
        // 当请求方法是POST,如果不指定content-type是其他类型的话,默认为如下,要求参数传递样式为 key1=value1&key2=value2,但实际场景以json为多
        // 'content-type': 'application/x-www-form-urlencoded',
      },
    };
    if (options.method === 'POST' || 'PUT') {
      defaultOptions.headers['Content-Type'] = 'application/json; charset=utf-8';
    }
    const newOptions = { ...defaultOptions, ...options };
    console.log('newOptions', newOptions);
    return fetch(url, newOptions)
      .then(checkStatus)
      .then(judgeOkState)
      .then(res => res.json())
      .catch(handleError);
  }

  /**
   *post请求方式
   * @param url
   * @returns {Promise<unknown>}
   */
  post(url, params = {}, option = {}) {
    const options = Object.assign({ method: 'POST' }, option);
    //一般我们常用场景用的是json,所以需要在headers加Content-Type类型
    options.body = JSON.stringify(params);

    //可以是上传键值对形式,也可以是文件,使用append创造键值对数据
    if (options.type === 'FormData' && options.body !== 'undefined') {
      let params = new FormData();
      for (let key of Object.keys(options.body)) {
        params.append(key, options.body[key]);
      }
      options.body = params;
    }
    return http.staticFetch(url, options); //类的静态方法只能通过类本身调用
  }

  /**
   * put方法
   * @param url
   * @returns {Promise<unknown>}
   */
  put(url, params = {}, option = {}) {
    const options = Object.assign({ method: 'PUT' }, option);
    options.body = JSON.stringify(params);
    return http.staticFetch(url, options); //类的静态方法只能通过类本身调用
  }

  /**
   * get请求方式
   * @param url
   * @param option
   */
  get(url, option = {}) {
    const options = Object.assign({ method: 'GET' }, option);
    return http.staticFetch(url, options);
  }
}

const requestFun = new http(); //new生成实例
export const { post, get, put } = requestFun;
export default requestFun;

定义接口文件,导出fetch

utils/api

import requestFun from "./fetchUtil"; //引入
import qs from "qs";

const { stringify } = qs;
const { post, get } = requestFun;

//get方式
export async function fetchData1(params) {
  return get(`/api/bbb?${stringify(params)}`);
}

//post方式
export async function fetchData2(params) {
  return post(`/api/aaa`, params);
}
 
export async function fetchGetComments(params) {
    return get(`http://localhost:8001/comments`, params);
}

组件内使用

home.js

import {fetchGetComments} from '../../utils/api'

// 获取数据
const loadData = async ()=> {
    const commentData = await fetchGetComments();
    console.log('commentData')
    console.log(commentData) 
}

延申问题

如果要扩展fetch的delete和patch,该如何做?

如果自定义header的话,该如何做?

如果post后端需要formdata结构数据又该如何做?

感兴趣的小伙伴,可以自行脑补一下。

参考文档


前端老兵
15 声望1 粉丝

你们说这是哪里?