从零实现axios(3.4小节-实现数据转换器2)

43 阅读3分钟

实现数据转换器2

我们在defaults目录下创建transitional.js文件,默认导出transitional配置对象

module.exports = {
  silentJSONParsing: true,
  forcedJSONParsing: true,
  clarifyTimeoutError: false
};

我们在helpers目录下创建normalizeHeaderName.js文件,并在里面实现normalizeHeaderName函数,该函数的作用就是标准化请求头字段,如把 content-type 标准化为 Content-Type

'use strict';

var utils = require('../utils');

module.exports = function normalizeHeaderName(headers, normalizedName) {
  // 遍历所有请求头
  utils.forEach(headers, function processHeader(value, name) {
    // 找到需要处理的请求头字段
    if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
      // 添加标准的请求头字段
      headers[normalizedName] = value;
      // 删掉原先不规范的请求头字段
      delete headers[name];
    }
  });
};

我们在defaults目录下创建env目录,在该目录里面创建FormData.js文件, 文件的代码非常简单,就是加载一个npm包

module.exports = require('form-data');

我们在helpers目录创建toFormData.js文件,在该文件实现toFormData函数。

'use strict';

var utils = require('../utils');

/**
 * 把 data 转换到 FormData
 * @param {Object} obj data数据
 * @param {?Object} [formData] formData对象
 * @returns {Object}
 **/

function toFormData(obj, formData) {
  formData = formData || new FormData();

  var stack = [];

  // 对值进行转换
  function convertValue(value) {
    if (value === null) return '';

    // 日期类型,需转为ISOString
    if (utils.isDate(value)) {
      return value.toISOString();
    }

    // buffer或类型数组,转为Blob类型,我们在这只实现浏览器部分,node部分忽略
    if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
      return new Blob([value])
    }

    return value;
  }

  // parentKey是父键,比如 {a: {b: 'v'}}, a就是{b: 'v'}的parentKey
  function build(data, parentKey) {
    if (utils.isPlainObject(data) || utils.isArray(data)) {
      // 循环引用,比如data的属性值引用了data本身:const data = {}, data.key = data
      if (stack.indexOf(data) !== -1) {
        throw Error('Circular reference detected in ' + parentKey);
      }

      // 类似于锁的作用,用于处理循环引用
      stack.push(data);

      utils.forEach(data, function each(value, key) {
        if (utils.isUndefined(value)) return;
        var fullKey = parentKey ? parentKey + '.' + key : key;
        var arr;

        if (value && !parentKey && typeof value === 'object') {
          if (utils.endsWith(key, '{}')) {
            // key以{}结尾,则对值进行stringify
            value = JSON.stringify(value);
          } else if (utils.endsWith(key, '[]') && (arr = utils.toArray(value))) {
            // key以[]结尾,通过utils.toArray把value转为数组,比如value可能是文件列表
            // 然后把数组里的值依次添加到formData
            arr.forEach(function(el) {
              !utils.isUndefined(el) && formData.append(fullKey, convertValue(el));
            });
            return;
          }
        }

        // 对值进行递归处理,假如value也是个纯对象,这样就可把纯对象里的值也添加到formData
        build(value, fullKey);
      });

      stack.pop();
    } else {
      formData.append(parentKey, convertValue(data));
    }
  }

  build(obj);

  return formData;
}

module.exports = toFormData;

我们接着实现 utils.js中的辅助函数

var toString = Object.prototype.toString;
// 判断值是否是函数
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

// 判断值是否是ArrayBuffer类型
var isArrayBuffer = kindOfTest('ArrayBuffer')

// 判断值是否是string类型
function isString(val) {
  return typeof val === "string";
}

// 判断值是否是文件类型
var isFile = kindOfTest('File');

//判断值是否是Blob类型
var isBlob = kindOfTest('Blob');

// 判断值是否是Stream类型
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}

// 判断值是否是URLSearchParams类型
var isURLSearchParams = kindOfTest('URLSearchParams')

// 判断值是否是文件列表类型
var isFileList = kindOfTest('FileList')

// 判断值是否是FormData类型
function isFormData(thing) {
  var pattern = '[object FormData]';
  return thing && (
    (typeof FormData === 'function' && thing instanceof FormData) ||
    toString.call(thing) === pattern ||
    (isFunction(thing.toString) && thing.toString() === pattern)
  );
}

// 判断值是否是Buffer类型
function isBuffer(val) {
  return (
    val !== null &&
    !isUndefined(val) &&
    val.constructor !== null &&
    !isUndefined(val.constructor) &&
    typeof val.constructor.isBuffer === "function" &&
    val.constructor.isBuffer(val)
  );
}

// 判断值是否是ArrayBufferView类型
function isArrayBufferView(val) {
  var result;
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    result = ArrayBuffer.isView(val);
  } else {
    result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
  }
  return result;
}

// 判断值是否是类型数组
var isTypedArray = (function(TypedArray) {
  // eslint-disable-next-line func-names
  return function(thing) {
    return TypedArray && thing instanceof TypedArray;
  };
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));


/*
 * 判断一个字符串是否以指定的字符结尾
 * @param {String} str 字符串
 * @param {String} searchString 是否以这个字符结尾
 * @param {Number} [position= 0] 指定结尾的位置
 * @returns {boolean}
 */
function endsWith(str, searchString, position) {
  str = String(str);
  if (position === undefined || position > str.length) {
    position = str.length;
  }
  position -= searchString.length;
  var lastIndex = str.indexOf(searchString, position);
  return lastIndex !== -1 && lastIndex === position;
}

// 把like object 转化为数组,e.g. {0: 'a', length: 1}
function toArray(thing) {
  if (!thing) return null;
  var i = thing.length;
  if (isUndefined(i)) return null;
  var arr = new Array(i);
  while (i-- > 0) {
    arr[i] = thing[i];
  }
  return arr;
}