xqy404.com

HTTP 请求中可以传递哪些类型的值

缘起于群友大佬的提问,所以来研究分析一下
2024-08-19

HTTP 请求中可以传递哪些类型的值

缘起于群友大佬的提问,所以来研究分析一下
8 min read
created on 2024-08-19
#http
#request
#type

起因是我在某前端群里吐槽工地后端同事:要求前端传递给后端 Date 类型的数据,然后后端返回数据为 Number 类型的时间戳……(当时我的血压真的很大),然后群友大佬提出了一个问题:“好奇,Date 类型怎么传?”

我:“好问题!把我给问住了……”

虽然我当时并不清楚具体是怎么传递的 Date 类型数据,但是我知道在我之前的实践中,直接传递一个 Date 类型是能正常请求的,所以在这个中间过程,一定发生了什么。之前一直当牛马一笑而过,现在还是来研究一下了。

HTTP JSON 请求

在极大多数情况下,我使用的是 axios 这个库来发送请求,但也都是发的 JSON 数据,所以先来看看最基础的 XMLHttpRequest 是怎么发送 JSON 数据的,叫 AI 帮我写了个 Demo

// 封装 XHR 请求函数
function makeRequest(method, url, data = null) {
  return new Promise((resolve, reject) => {
    // 创建 XHR 对象
    const xhr = new XMLHttpRequest();

    // 打开连接
    xhr.open(method, url, true);

    // 设置请求头
    xhr.setRequestHeader('Content-Type', 'application/json');

    // 处理加载完成事件
    xhr.onload = function() {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };

    // 处理网络错误
    xhr.onerror = function() {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };

    // 发送请求
    xhr.send(data ? JSON.stringify(data) : null);
  });
}

// 使用示例
// GET 请求
makeRequest('GET', 'https://api.example.com/users')
  .then(data => console.log('Users:', data))
  .catch(error => console.error('Error:', error));

// POST 请求
const userData = { name: 'John Doe', email: '[email protected]' };
makeRequest('POST', 'https://api.example.com/users', userData)
  .then(response => console.log('User created:', response))
  .catch(error => console.error('Error creating user:', error));

从 Demo 中可以看出,在 xhr.send(data ? JSON.stringify(data) : null); 发送请求的时候,data 是有被 JSON.stringify 处理的,所以在请求中传递的数据类型是 JSON 字符串。

再来看看 fetch 是怎么发送请求的:

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(userData)
})
  .then(response => response.json())
  .then(data => console.log('User created:', data))
  .catch(error => console.error('Error creating user:', error));

如出一辙,fetch 也是通过 JSON.stringify 处理数据的。

那么问题来了,JSON 字符串中可以包含哪些类型的数据呢?

JSON 数据类型

先去看看 MDN 上的定义:

JSON.stringify() 将值转换为相应的 JSON 格式:

  • 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
  • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
  • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如 JSON.stringify(function()) or JSON.stringify(undefined).
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
  • Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString()),因此会被当做字符串处理。
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null。
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。

from JSON.stringify() - JavaScript | MDN

从上面的定义中可以得出结论,JSON 字符串中可以包含的数据类型有:

  1. 字符串
  2. 数字
  3. 布尔值
  4. 对象(Object)
  5. 数组(Array)
  6. null

那么 Date 类型的数据是怎么传递的呢?

它将会被 toJSON() 方法转换为字符串,所以在 JSON 字符串中,Date 类型的数据是字符串类型的。

const date = new Date();
console.log(JSON.stringify(date)); // "2024-08-19T08:00:00.000Z"
// 等同于
console.log(date.toJSON()); // "2024-08-19T08:00:00.000Z"

所以其实这么一看,Date 类型的数据是可以直接传递的,只不过传递的过程中被转化为字符串类型的数据。

HTTP 请求

再来看看 HTTP 请求有哪些类型。

像刚刚的 JSON 请求:

  1. Content-Type: application/json 的 POST 请求
  2. Accept: application/json 的 GET 请求

同类的请求包括:

  • XML 请求 (Content-Type: application/xml)
  • Form Data 请求 (Content-Type: application/x-www-form-urlencoded)
  • Multipart Form Data 请求 (Content-Type: multipart/form-data)
  • Plain Text 请求 (Content-Type: text/plain)

让我们对这些请求类型进行比较:

// 使用 Fetch API 进行请求对比

// 1. JSON 请求
const jsonRequest = async (url, data) => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
  });
  return response.json();
};

// 2. XML 请求
const xmlRequest = async (url, xmlString) => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/xml',
    },
    body: xmlString,
  });
  return response.text(); // XML 响应通常作为文本处理
};

// 3. Form Data 请求
const formDataRequest = async (url, data) => {
  const formData = new URLSearchParams(data);
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: formData,
  });
  return response.json();
};

// 4. Multipart Form Data 请求
const multipartFormDataRequest = async (url, data) => {
  const formData = new FormData();
  for (const [key, value] of Object.entries(data)) {
    formData.append(key, value);
  }
  const response = await fetch(url, {
    method: 'POST',
    body: formData, // 不需要设置 Content-Type,浏览器会自动设置
  });
  return response.json();
};

// 5. Plain Text 请求
const plainTextRequest = async (url, text) => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain',
    },
    body: text,
  });
  return response.text();
};

比较分析:

  1. JSON 请求:

    • 优点:数据结构清晰,易于解析,广泛支持
    • 缺点:不支持二进制数据,大型数据可能效率较低
  2. XML 请求:

    • 优点:支持复杂的数据结构,自描述性强
    • 缺点:解析相对复杂,数据量较大
  3. Form Data 请求:

    • 优点:简单,兼容性好
    • 缺点:不适合复杂的数据结构
  4. Multipart Form Data 请求:

    • 优点:支持文件上传,可以混合不同类型的数据
    • 缺点:请求体较大,不适合频繁的小数据传输
  5. Plain Text 请求:

    • 优点:简单,适合传输纯文本数据
    • 缺点:不适合结构化数据,解析可能需要额外工作

所以 JSON 请求被广泛使用,但是在特定场景下,其他类型的请求也是有用武之地的。