axios-ajax-WebSocket紧急救援

接口交互效果

1、ajax

终止请求:abort()

一个简单的ajax,仅供入门学习使用

/**
 * 调用SendAjax,调用接口
 * @param {obj} object method:请求方式,url:地址,success:请求成功回调
 */

function SendAjax (obj) {
    function createAJAX () {
        let ajax = null
        if (window.XMLHttpRequest) {
            ajax = new XMLHttpRequest() // iE7以上
        } else if (window.ActiveXObject) {
            const ActiveXObject = window.ActiveXObject // iE6到iE5以下
            ajax = new ActiveXObject('Microsoft.XMLHTTP')
        } else {
            alert('请升级浏览器')
        }
        return ajax
    }
    var ajax = createAJAX() // 创建一个ajax对象
    ajax.open(obj.method, obj.url, true)
    ajax.onreadystatechange = function () {
        if (ajax.readyState === 4 && ajax.status === 200) {
            obj.success(ajax.responseText)
        }
    }
    if (obj.method === 'get') {
        ajax.send(null)
    } else if (obj.method === 'post') {
        ajax.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
        let str = ''
        let v = ''
        let f = false
        for (var attr in obj.data) {
            str += v + attr + '=' + obj.data[attr]
            v = '&'
            if (obj.data[attr] !== '') {
                f = true
            } else if (obj.data[attr] === '') {
                f = false
                return f
            }
        }
        if (f) {
            ajax.send(str)
        }
    }
}

export default SendAjax()

2、JSONP

跨越:协议,域名,端口号不同,引发的安全请求问题

img标签的src可以解决上面的问题,但是请求回来的是图片信息,不能使用

同理还有video标签等等

script标签的src请求回来的是js代码信息,可以作为跨越解决方案

但是返回的信息如何获取?可以通过函数调用的方式获取,调用函数,传入入参

  • 编写获取信息的回调函数
  • 同态生成script标签
  • 将请求地址+query传参方式将回调函数名称传给服务
  • 将生成的script标签前进给body
  • 服务将返回内容以callback(数据信息)的形式返回给浏览器
  • 实现跨越请求信息
// 粗劣残暴的解决方案
// params可以优化成对象
/**
* param { url } 跨越请求的地址
* param { params } 拼接的字符串入参
* param { fn } 获取返回结果的回调
*/
export default function jsonp(url, params, fn) {
  let callbackName = `fn${+new Date()}`
  window[callbackName] = (res) => { fn(res) }
  let script = document.createElement('script')
  script.type = "text/javascript";
  script.src = `${url}?callback=${callbackName}&${params}`
  document.body.appendChild(script)
  document.body.removeChild(script)
}

3、axios

实现功能:

  • post请求,get请求等基本请求

  • 请求前拦截,请求后拦击

  • 设置全局devServe识别标志(baseUrl),设置全局timeout响应时间

  • 拦截前操作:

    1. 添加请求网关,有需要自定义的使用自定义,没有使用全局网关

    2. 添加token,有不需要tiken的可以添加自定义标识,进行判断

    3. 设置下载文件名称,有自定义下载文件名称,赋值给全局变量。

    4. 单独处理报错,有自定义处理报错消息,修改全局变量 处理报错标识

    5. 单独设置超时,有自定义的使用自定义,没有就使用全局超时时间

    6. 参数转get,保持请求函数统一性,get和params入参方式统一

    7. 参数转表单形式,处理参数以表单形式传入

    8. 获取请求进度,添加获取下载/上传进度条的构造函数

    9. 取消请求,取消当前进行的全部请求

      (⭐:未设计和验证)为了在切换路由时或特别情况时主动中断请求

  • 请求后拦截:

    1. 根据res.header判断文件类型,进行预处理。

      如:二进制流图片,可以直接使用,二进制图片转图片url,进行处理,然后返回处理结果

    2. 根据全局变量-自定义报错处理,判断是否需要直接返回请求结果

    3. 判断是否为未登录

      是:统一报错,return false,并调整到登录页面

    4. 判断后端返回的状态码是否通过判断

      是:返回结果,return data

      否:进入下一步

    5. 未通过判断,是否存在单独设置获取错误表示

      是:return Promise.reject(data)

      否:进行统一报错,return false

  • 其他请求处理

    1. post请求形式下载
    2. get请求形式下载
    3. jsonxlsx下载功能
    4. post文件和数据分开上传
    5. post文件和数据合成formData一起上传
    6. xlsxjson导入功能

可以直接复制,建议将代码看完后,修给后使用

import axios from "axios";
import Qs from "qs";
import * as xlsx from "xlsx";

// 构造axios封装函数
function setAjaxHttp({
  preFix,
  baseURL,
  timeout,
  fileTypes,
  Message,
  handleResponse,
}) {
  // 定义入口网关
  const prefix = preFix || "";

  const ajax = axios.create(); // 实例化一个axios

  if (!preFix && baseURL) ajax.defaults.baseURL = baseURL; // 设置接口的域名和端口号

  let setFileName = false; // 自定义文件下载名称
  let gainError = false; // 单独处理错误标记

  // 添加请求webpack-serve前缀/接口请求网关
  function getBaseUrl(config) {
    // 单独接口使用 请求自定义 服务器地址、端口、接口网关
    if (config.data && config.data.baseUrl) {
      config.url = config.data.baseUrl + config.url;

      // 如果存在自定义下载文件名称
      delete config.data.baseUrl;
    } else {
      // 否则使用环境变量判断得到的,走代理域名和端口
      config.url = prefix + config.url;
    }
    return config;
  }

  // 设置token
  function setToken(config) {
    if (config.data && config.data.setToken) {
      ajax.thisToken = config.data.setToken;
      delete config.data.setToken;
      return config;
    }
  }

  // 添加token
  function addToken(config) {
    // 如果注明不需要token,这不添加token,直接返回config对象
    if (config.data && config.data.withoutToken) {
      delete config.data.withoutToken;
      return config;
    }

    // 添加token
    if (ajax?.thisToken && !config.headers?.token)
      config.headers.token = ajax?.thisToken;
    return config;
  }

  // 设置下载文件名称
  function setDownFileName(config) {
    setFileName = false;

    // 如果存在 自定义 下载文件名称
    if (config.data && config.data.setFileName) {
      setFileName = config.data.setFileName;
      delete config.data.setFileName;
    }
    return config;
  }

  // 单独处理错误标记
  function setGainError(config) {
    gainError = false;

    // 如果存在 单独处理错误标识
    if (config.data && config.data.gainError) {
      gainError = config.data.gainError;
      delete config.data.gainError;
    }
    return config;
  }

  // 单独设置超时时间
  function setTimeOut(config) {
    if (config.data && config.data.timeout) {
      config.timeout = config.data.timeout || timeout;
      delete config.data.timeout;
    } else {
      config.timeout = timeout;
    }
    return config;
  }

  // 转换get入参
  function paramsSwitchToQuery(config) {
  	if (config.method === 'get') {
      config.params = config.data || config.params;
      config.data = null;
      config.paramsSerializer = (params) => {
        return Qs.stringify(params, { arrayFormat: 'brackets', strictNullHandling: true })
      }
    }

    return config
  }

  // 获取进度条
  function getAjaxRange(config) {
    if (config.data && config.data.getRange) {
      // 获取上传/下载进度条
      let getRange = config.data.getRange;
      delete config.data.getRange;

      // 将上传的formData直接当作入参
      if (config.data.formData) config.data = config.data.formData;

      const fn = (progress) => {
        // 有时候拿不到total值,这个值总是为0
        // 只要设置后台的代码  response.setContentLengthLong(文件长度); 就可以了
        getRange?.fn(Math.round((progress.loaded / progress.total) * 100));
      };

      if (getRange.type === "upload") config.onUploadProgress = fn;
      if (getRange.type === "download") config.onDownloadProgress = fn;
    }
    return config;
  }

  // 转换为表单格式请求
  function switchToFormData(config) {
    // 如果需要转换成表单格式
    if (config.data && config.data.transform) {
      delete config.data.transform;

      // 通过axios提供的transformRequest 修改入参格式
      config.transformRequest = [
        function (data) {
          return Qs.stringify(data);
        },
      ];
    }
    return config;
  }

  // 取消请求
  const source = axios.CancelToken.source(); // 创建CancelToken
  function cancelAjax(config) {
    // 切换页面时取消请求 主要是优化
    config.cancelToken = source.token;
    return config;
  }

  // 使用案例:
  // 注解:没必要详细到单独接口,毕竟web端接口量特别大,精准控制不合适
  // 外部引入,调用ajax.cancelAjax,取消请求
  ajax.cancelAjax = function (word = "Operation canceled by the user") {
    source.cancel(word);
  };

  // 请求拦截 - 在发送请求之前做些什么
  ajax.interceptors.request.use(
    (config) => {
      getBaseUrl(config); // 添加请求webpack-serve前缀/接口请求网关
      setToken(config);
      addToken(config); // 添加token
      setDownFileName(config); // 设置下载文件名称
      setGainError(config); // 单独处理错误标记
      setTimeOut(config); // 单独设置超时时间
      getAjaxRange(config); // 获取进度条
      paramsSwitchToQuery(config) // 转换get入参
      switchToFormData(config); // 转换为表单格式请求
      cancelAjax(config); // 取消请求

      return config;
    },
    (error) => Promise.reject(error)
  );

  // 响应拦截
  ajax.interceptors.response.use(
    (res) => {
      if (res?.header) {
        // 处理流数据的时候,直接返回结果,用于下载
        if (res.headers["content-type"].includes("image")) {
          return getBase64Image(res.data);
        } else if (fileTypes.includes(res.headers["content-type"])) {
          return res;
        }
      }

      // 调用传入的参数处理函数
      return handleResponse(res?.data, gainError);
    },
    (error = "error") => {
      if (error.stack.indexOf("timeout") > -1) {
        Message && Message(error);
      } else if (error.msg !== undefined) {
        Message && Message(error);
      }
      return Promise.reject(error);
    }
  );

  // 图片 二进制转图片url
  function getBase64Image(img) {
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, img.width, img.height);
    var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
    var dataURL = canvas.toDataURL("image/" + ext);
    return dataURL;
  }

  // post请求下载文件
  // 默认download
  ajax.download = function (url, params = {}) {
    return ajax({
      method: "post",
      url,
      data: {
        ...params,
        getRange: {
          fn: params.getRange || null,
          type: "download",
        },
      },
      headers: {
        "X-Requested-With": "XMLHttpRequest",
      },
      responseType: "blob",
    })
      .then(disposeRes)
      .catch((error) => {
        return Promise.reject(error);
      });
  };

  // get请求下载文件
  ajax.downloadFileByGet = function (url, params = {}) {
    return ajax({
      method: "get",
      url,
      params: {
        ...params,
        getRange: {
          fn: params.getRange || null,
          type: "download",
        },
      },
      headers: {
        "X-Requested-With": "XMLHttpRequest",
      },
      responseType: "blob",
    })
      .then(disposeRes)
      .catch((error) => {
        return Promise.reject(error);
      });
  };

  // 将json数据转换.xlsx下载
  // 待验证
  /**
   *
   * @param { json } JsonData // json数据
   * 案例:[{name: '旭大王', age: 12}, {name: '旭小王', age: 21}]
   * @param { object } columns // 表头设置
   * 对应案例:{ name: '姓名', age: '年龄' }
   * @param { string } FileName // 文件名称
   */
  ajax.jsonToXlsx = function (JsonData, columns, FileName) {
    // json数据转excel
    // 先转化json
    var arrData =
      typeof JsonData !== "object" ? JSON.parse(JsonData) : JsonData;
    var excel = "";var row ="";// 设置表头var keys = Object.keys(JsonData[0]);
    keys.forEach(function(item){if(columns) row +="";else row +="";});// 换行
    excel += row +"";// 设置数据for(var i =0; i < arrData.length; i++){let row ="";for(var index in arrData[i]){
        row +="";}
      excel += row +"";}

    excel +="
" + columns[item] + " " + item + "
" + arrData[i][index] + "
"
; var excelFile = ""; excelFile += ''; excelFile += '; excelFile += '; charset=UTF-8">'; excelFile += ""; excelFile += ""; excelFile += ""; excelFile += ""; excelFile += excel; excelFile += ""; excelFile += ""; var uri = "data:application/vnd.ms-excel;charset=utf-8," + encodeURIComponent(excelFile); var link = document.createElement("a"); link.href = uri; link.style = "visibility:hidden"; link.download = FileName + ".xls"; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; // 处理下载文件返回数据 function disposeRes(res) { // 获取文件名称 let fileName = res.headers["content-disposition"].split("filename=")[1]; if (setFileName) fileName = setFileName; // 解码文件名称 fileName = decodeURI(fileName); // 将后端返回的二进制流转换为下载链接 let blobUrl = new Blob([res.data], { type: "application/octet-stream;charset=utf-8", }); if ("download" in document.createElement("a")) { // 动态生成a变迁,使用a标签的download属性完成下载 const eLink = document.createElement("a"); const href = URL.createObjectURL(blobUrl); eLink.download = fileName; eLink.style.display = "none"; eLink.href = href; document.body.appendChild(eLink); eLink.click(); URL.revokeObjectURL(href); document.body.removeChild(eLink); } else { // 如果a标签没有download属性,直接调用window属性保存到本地 navigator.msSaveBlob(blobUrl, fileName); } } /** * 文件上传 * 获取到上传的文件,并进行入参拼接 */ // 文件上传(可批量携带formData, 文件和数据分开传递, 且文件流入参key是固定的file) ajax.uploadFile = function (url, file, data = {}) { // 创建FormData对象 const formData = new FormData(); // 创建进度条控制对象 let getRange = { fn: data.getRange || null, type: "upload", }; delete data.getRange; let upLoadTimeout = timeout; // 将获取的文件添加到formData,不同组件上次的文件可能不同 if (file instanceof Array) { file.forEach((item) => { formData.append("file", item.raw, item.name); }); } else { formData.append("file", file.raw, file.name); } // 如果存在传递参数 if (Object.toString.call.apply(data) === "[object Object]") { if (data.timeout) { upLoadTimeout = data.timeout; delete data.timeout; } for (let key in data) { formData.append(`${key}`, data[key]); } } let config = { headers: { "Content-Type": "multipart/form-data", }, timeout: upLoadTimeout, }; return ajax.post(url, { getRange, formData }, config); }; // 携带参数上传(参数和文件一起传递) ajax.uploadFileWithPost = function (url, params = {}) { const formData = new FormData(); let getRange = null; if ( params.getRange && Object.prototype.toString.call(params.getRange) === "[object Function]" ) { getRange = { fn: params.getRange, type: "upload", }; delete params.getRange; } let upLoadTimeout = timeout; if (params.timeout) { upLoadTimeout = params.timeout; delete params.timeout; } appendParamsToFormData(formData, params); // 对象递归判断转平级 let config = { headers: { "Content-Type": "multipart/form-data", }, timeout: upLoadTimeout, }; return ajax.post(url, { getRange, formData }, config); }; // 将.xlsx转换为json数据导入 /** * * @param { file } file // 上传的文件 * @param { array } columns // 表头设置, * 案例[{label: '名称', key: 'name'}, {label: '年龄', key: 'age'}] * label与xlsx的表头对应,key为转换的json数据的keyName * @returns 返回转换的json */ ajax.xlsxToJson = async function (file, columns) { return new Promise((resolve) => { var reader = new FileReader(); reader.onload = function (e) { var data = e.target.result; var wb = xlsx.read(data, { type: "binary" }); var xlsxData = xlsx.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); // wb.SheetNames[0]是获取Sheets中第一个Sheet的名字 // wb.Sheets[Sheet名]获取第一个Sheet的数据 xlsxData = xlsxData.map((data) => { let obj = {}; for (let key in data) { let findObj = columns.find((item) => item.label === key); if (typeof data[key] === "string") { obj[findObj.key] = data[key].replace(/(^\s*)|(\s*$)/g, ""); // 去除左右空格 } else obj[findObj.key] = data[key]; } return obj; }); resolve(xlsxData); }; reader.readAsBinaryString(file); }); }; // 对象递归判断转平级 function appendParamsToFormData(formData, params) { for (let key in params) { let value = params[key]; let prototypeStr = Object.prototype.toString.call(value); if (prototypeStr === "[object File]") { // 更具具体key值传递文件流,文件名称需要打印输出判断 formData.append(key, value, value.name); // 将内部对象值递归转出化平级 } else if (prototypeStr === "[object Object]") { appendParamsToFormData(formData, value); } else { formData.append(key, value); } } } return ajax; } export default setAjaxHttp; // 下面是使用案例 import setAjaxHttp from "../utils/http"; import router from "@/router/index"; import { MessageBox, Message } from "element-ui"; // 统一处理错误函数 function handleResponse(data, gainError) { if (data.status !== 1) { if (gainError) return Promise.reject(data); // 401: 无效token; 402: 异地登陆; 403: token过期; let isTokenError = ["401", "402", "403"].includes(data.status); if (isTokenError) { MessageBox && MessageBox.confirm( "您已退出登录,您可以取消以留在此页面,或重新登录", "重新登陆", { confirmButtonText: "重新登录", cancelButtonText: "取消", type: "warning", } ).then(() => { router.push("/login"); }); return Promise.reject(data); } } return data; } // 导出实例化后的ajax const http = setAjaxHttp({ preFix: "/api", timeout: 5000, fileTypes: [ "multipart/octet-stream;charset=utf-8", "multipart/form-data", "multipart/octet-stream", "octets/stream;charset=UTF-8", ], Message, handleResponse, }); // 下面是http使用案例 export const usersFind = (params) => http.get("/users/find", { params }); export const usersAdd = (params) => http.post("/users/add", params);

4、WebSocket

  1. 创建链接对象:var Socket = new WebSocket(url, [protocol] );

    var ws = new WebSocket('ws://localhost:8001');
    

    url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

  2. 只读属性:

    • console.log(Socket.readyState)

    0 - 表示连接尚未建立, 1 - 表示连接已建立, 2 - 表示连接正在进行关闭,3 - 表示连接已经关闭

    • console.log(Socket.bufferedAmount)

    正在加载的队列

  3. 绑定事件:

    • Socket.onopen = fn: 连接建立时触发
    • Socket.onmessage = fn:客户端接收服务端数据时触发
    • Socket.onerror = fn: 通信发生错误时触发
    • Socket.onclose = fn:连接关闭时触发
  4. 方法:

    • Socket.send(src):使用连接发送数据
    • Socket.close():关闭连接
  5. 示例

    var ws = new WebSocket("ws://localhost:9998/echo");
    ws.onopen = function() {
      // Web Socket 已连接上,使用 send() 方法发送数据
      ws.send("发送数据");
      alert("数据发送中...");
    };
    
    ws.onmessage = function (evt) { 
      var received_msg = evt.data;
      alert("数据已接收...");
    };
    
    ws.onclose = function() { 
      // 关闭 websocket
      alert("连接已关闭..."); 
    };
    
  6. 进阶

    发生图片数据

    ws.onmessage = function (e) {
        mess.innerHTML = "连接成功"
        document.querySelector(".kuang").onclick = function (e) {
          var time = new Date();
          ws.send(time + "  game1点击了“" + e.target.innerHTML + "”");
        }
    
        var inputElement = document.getElementById("file");
        inputElement.onchange = function (e) {
          var file = (e.target.files)[0]
          ws.send(file);
        }
    }
    

5、uni-request

/**
  请求函数封装

  使用格式
  @param { String } api // 接口名称
  @params data // 入参合集, 含有:入参,自定义网关前缀,是否携带token(boolean),是否表单提交(boolean),单独定义的timeout,自定义responseType
  request.post(api, { params, [baseUrl], [withoutToken], [transform], [timeout], [responseType] })

  其他同理使用,文件上传可以将入参放在formData关键字,也可以直接入参
  入参文件可以使用files关键字传递文件,按照uni-app官方上传文件文档操作即可

  目录:
    入参加密
    自定义修改baseUrl
    自定义是否添加token
    是否转换为表单请求
    单独接口自定义timeout
    自定义responseType
    处理文件上传
    数据请求
*/

import store from '../store/index' // 引入vuex, 处理token,token加密,请求操作函数
import { router } from '../router' // 引入router,token失效跳转登录页面
import refreshToken from './refreshToken' // 刷新token函数(预留函数)
import Qs from 'qs' // 引入Qs

// showError: 全局错误信息处理函数
// requestListAdd: 请求操作函数队列新增, get读取,del删除
import { showError, requestListAdd, requestListGet, requestListDel, checkDataType } from './utils'

const prefix = process.env.NODE_ENV === 'development' ? '/api' : '/mpas' // 通过环境变量判断请求前缀
const refreshLogin = false // 不自动刷新登录状态
const timeoutDefault = 1000 * 60 // 预设超时时间


export default {
  get(api, data) { return this.theRequest({ api, data, method: "get" }) }, // 进行get请求
  post(api, data) { return this.theRequest({ api, data }) }, // 进行post请求
  upload(api, data) { return this.theRequest({ api, data, useType: "uploadFile" }) }, // 进行post文件上传
  download(api, data) { return this.theRequest({ api, data, useType: "downloadFile" }) }, // 进行post文件下载

  
  // 自定义修改baseUrl
  addBaseUrl(args) {
    // 特定自定义过服务识别和网关接口,使用自定义的
    if (args.data && args.data.baseUrl) {
      args.url = args.data.baseUrl + args.url
      delete args.data.baseUrl
    } else {
        // 否则使用环境变量判断得到的
        args.url = prefix + args.url
    }
    return args
  },
 
  // 自定义是否添加token
  addToken(args) {
    // 如果注明不需要token,这不添加token,直接返回config对象
    if (args.data && args?.data?.withoutToken) {
      delete args?.data?.withoutToken
      return args
    } else if (store.state.app.token) {
      if (refreshLogin) refreshToken() // 是否刷新token
      // 添加token - 这里没有将token放在请求头
      args.url = args.url + '?id_token=' + store.state.app.token
    }
    return args
  },

  // 是否转换为表单请求
  switchToFormData(args) {
    if (args.data && args.data.transform) {
      delete args.data.transform
      args.data = Qs.stringify(args.data)
    }
    return args
  },

  // 单独接口自定义timeout
  setTimeout(args) {
    if (args.data && args.data.timeout) {
      args.timeout = args.data.timeout
      delete args.data.timeout
    } else {
      args.timeout = timeoutDefault
    }
    return args
  },

  // 自定义responseType
  setDataType(args) {
    if (args.data && args.data.responseType) {
      args.responseType = args.data.responseType
      delete args.data.responseType
    }
    return args
  },

  // 处理文件上传
  setFormData(args, useType) {
    if (useType !== 'uploadFile') return args
    
    // 处理文件
    if (args.data && args.data.files) {
      args.files = args.data.files
      delete args.data.files
    }

    // 将参数用formData上传(虽然没有必要)
    if (args.data) {
      args.formData = args.data
      delete args.data
    }

    return args
  },

  // 数据请求
  theRequest({ api, data, method = "post", useType = "request" }) {
    let _this = this // 修改this名称
    let gainError = false // 是否需要自定义处理错误
    let isDirect = false // 是否直接返回结果不进行拦截处理
    let getFunction = uni[useType] // 同态获取请求方法
    let requestTask = '' // 用来获取请求中操作函数

    // 需要返回的promise
    const obj = new Promise((resolve, reject) => {
      // 进行接口请求前拦截
      uni.addInterceptor(useType, {
        // 接口请求前拦截
        invoke(args) {
          _this.addBaseUrl(args) // 自定义修改baseUrl

          _this.addToken(args) // 自定义是否添加token

          _this.switchToFormData(args) // 是否转换为表单请求

          _this.setTimeout(args) // 自定义timeout

          _this.setDataType(args) // 自定义responseType

          _this.setFormData(args, useType) // 处理文件上传

          _this.encryption(args, method) // 自动入参加密

          // 是否需要全局拦截
          if (args.data && args.data?.gainError) {
            gainError = args?.data?.gainError
            // 每次调用theRequest会刷新gainError,每个接口的状态不一样
            // 不用担心gainError状态持续为true
            delete args?.data?.gainError
          }

          // 是否需要直接返回结果
          if (args.data && args.data?.isDirect) {
            isDirect = args?.data?.isDirect
            delete args?.data?.isDirect
          }
        },

        // 接口链接成功函数 - 接口请求后拦截
        success: (res) => {
          // 删除当前完成接口控制函数
          requestListDel(api)

          if (isDirect) return resolve(res) // 直接返回结果

          if (res.statusCode === 401) {
            uni.showToast({ icon: 'none', title: '登录过期'})
            router.push('/pages/login/index')
            reject(res)

          } else if (res.statusCode === 200) {
            // 如果返回的接口为json字符串
            if (checkDataType(res.data, 'string')) res.data = JSON.parse(res.data)
            const { code, msg, errMsg } = res.data
            if (code === 1) resolve(res) // 接口请求成功
            else if (gainError) reject(res)
            else uni.showToast({ icon: 'none', title: msg || errMsg || "网络链接异常" })
          
          } else {
            // 结果返回的请求错误信息
            if (gainError) reject(res)
            else {
              const { msg, errMsg } = res.data
              uni.showToast({ icon: 'none', title: msg || errMsg || "网络链接异常" })
            }
          }
        },

        // 接口链接失败函数
        fail: (err) => {
          if (gainError) reject(err)
          else showError(err) // 注意,后续如果需要接口请求超时,在showError里添加超时判断
        }
      })

      // 进行接口请求
      requestTask = getFunction({
        url: api,
        data,
        method,
        header: {}
      })

      // 将操作函数存储到store中
      let isExist = requestListGet(api)
      if (!isExist) { // 如果没有,进行新增。
        requestListAdd({ key: api, fn: requestTask })
      } else { // 如果有,停止前一个, 由于停止后即删除,所以需要重新新增
        if (isExist?.abort) isExist?.abort()
        requestListDel(api)
        requestListAdd({ key: api, fn: requestTask })
      }
      
      // 清除接口请求前拦截 - 监听类似于定时器,必须要进行清除
      uni.removeInterceptor(useType)
    })

    // 返回promise
    return obj
  }
}

6、关于图片/文件

  • 无论是返回图片链接还是图片下载,都必须设置下面的步骤

  • 无论是git还是post请求,请求都必须设置responseType: 'blob',

  • 上一步不设置,无法获取正确的二进制流数据结果

  • 二进制图片转换

    let binaryData = [];
    binaryData.push(blob);
    // 其实window.URL.createObjectURL(blob)就可以了
    // Chrome更新后不支持这种用法, 需要添加上面的东西。
    link.href = window.URL.createObjectURL(new Blob(binaryData));
    
  • 文件资源

    let fileName = res.headers['content-disposition'].split('filename=')[1]
    fileName = decodeURI(fileName)
    let blob = new Blob([res.data], { type: 'application/octet-stream;charset=utf-8' })
    const href = URL.createObjectURL(blob)
    
    const elink = document.createElement('a')
    elink.download = fileName
    elink.style.display = 'none'
    elink.href = href
    document.body.appendChild(elink)
    elink.click()
    URL.revokeObjectURL(href)
    document.body.removeChild(elink)
    

你可能感兴趣的:(前端学习,ajax)