Skip to content

FormData的使用及请求头分析

本文核心内容:

  1. FormData简介
  2. FormData的使用(jQuery、XHR、fetch、axios,及对应源码解析)
  3. 请求头分析

一、FormData简介

XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个"表单".比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件. ——https://developer.mozilla.org/zh-CN/docs/Web/API/FormData

二、FormData的使用 要构建一个FormData数据的话,只需要用类似下面的伪代码:

js
const fd = new FormData();
fd.append("key", "valueOfTheKey");
// 注意,传文件时,第二个参数是一个文件blob对象,可以通过传入第三个参数来指定文件名(filename)
fd.append("file", fileblob, "optionalFileName");

跟jQuery一起使用: 注意:要发送FormData数据的话,jquery ajax的配置里processData和contentType的值需要显性得设置为false。

js
$.ajax(
  merge(
    {
      type: "POST",
      accepts: "application/json",
      contentType:
        requestData instanceof FormData
          ? false
          : "application/x-www-form-urlencoded;charset=utf-8",
      url: apiPrefix + endPoint,
      dataType: "json",
      processData: !(requestData instanceof FormData),
      data: (() => {
        if (requestData instanceof FormData) {
          return requestData;
        }
        const d = new Date().getTime();
        return {
          requestTime: d,
          sessionToken: hexMd5((d + "").substring((d + "").length - 8)),
          requestBody: JSON.stringify(requestData),
        };
      })(),
    },
    options,
  ),
);

跟axios一起使用:

js
export const postData = (
  url: string,
  requestData: Object,
  ajaxHeaderOptions?: Object,
): any => {
  return axios({
    ...axiosConfig,
    url,
    method: 'post',
    data: (() => {
      if (requestData instanceof FormData) {
        return requestData;
      } else {
        return qs.stringify((() => {
          const d = new Date().getTime();
          return {
            requestTime: d,
            sessionToken: hexMd5((d + '').substring((d + '').length - 8)),
            requestBody: JSON.stringify(requestData),
          }
        })());
      }
    })(),
    headers: {
      'Accept': 'application/json',
      'X-Requested-With': 'XMLHttpRequest',
      ...(() => {
        if (requestData instanceof FormData) {
          return {};
        } else {
          return { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' };
        }
      })(),
      ...(ajaxHeaderOptions || {}),
    },
  }).catch((error: any): any => {
    if (error.response) {
      // The request was made and the server responded with a status code
      // that falls out of the range of 2xx
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // The request was made but no response was received
      // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
      // http.ClientRequest in node.js
      console.log(error.request);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }
    console.log(error.config);
  })
};

三、请求头分析 这块主要的内容就是请求头里Content-Type里有个boundary,它的值在Request Payload里出现了好多次。它的作用就是作为请求参数里各个key-value对的分界线(boundary)。前文我们有说过append方法的第三个参数可以指定filename,如果我们没传第三个参数的话,像上面截图里filename="blablabla.jpeg"的部分就不会出现了。有时候这个地方接口联调失败有可能是后台取了filename但是前端没有指定filename。

天上的神明与星辰,人间的艺术和真纯,我们所敬畏和热爱的,莫过于此。