Request 和 Response 都使用了 Fetch API 的 Body 混入

Request 和 Response 使用 Fetch API 的 Body 混入是什么意思?

在 Fetch API 中,"Body 混入"(Body Mixin)是一个重要的概念。

什么是混入(Mixin)

混入是一种面向对象编程的概念,允许将一个对象的属性和方法"混入"到另一个对象中,实现代码复用。

在 Fetch API 中,Body 混入提供了一组处理 HTTP 消息体的方法和属性。

这个混入为两个类型(Request 和 Response)提供了:

  1. 只读的 body 属性(实现为ReadableStream)
  2. 只读的 bodyUsed布尔值(表示body流是否已读)
  3. 一组方法(用于从流中读取内容并将结果转换为某种JavaScript对象类型)

Body混入提供了5个方法,用于将ReadableStream转存到缓冲区的内存里,将缓冲区转换为某种JavaScript对象类型,以及通过期约来产生结果。

在解决之前,期约会等待主体流报告完成及缓冲被解析。

这意味着客户端必须等待响应的资源完全加载才能访问其内容。

Body 混入包含的属性和方法

  1. body 属性 - 一个只读的 ReadableStream,表示响应/请求的内容

  2. bodyUsed 属性 - 布尔值,表示 body 是否已被读取

  3. arrayBuffer() 方法 - 读取 body 并返回 ArrayBuffer 格式的 Promise

  4. blob() 方法 - 读取 body 并返回 Blob 格式的 Promise

  5. formData() 方法 - 读取 body 并返回 FormData 格式的 Promise

  6. json() 方法 - 读取 body 并返回 JSON 解析后的 Promise

  7. text() 方法 - 读取 body 并返回文本格式的 Promise

为什么 Request 和 Response 都使用 Body 混入

  1. 代码复用 - Request 和 Response 都需要处理 HTTP 消息体,使用相同的接口

  2. 一致性 - 提供统一的 API 来处理请求体和响应体

  3. 功能共享 - 无论处理请求还是响应,都可以使用相同的方法读取和解析数据

实际意义

  • Request 对象可以使用 bodyUsed、json() 等方法处理请求体

  • Response 对象可以使用 bodyUsed、json() 等方法处理响应体

  • 两者共享相同的处理逻辑和 API

示例:Response 对象使用 Body 混入的方法

javascript 复制代码
// 示例:Response 对象使用 Body 混入的方法
console.log('=== Response 对象的 Body 混入 ===');
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
  // 检查 Body 混入的属性
  console.log('Response body:', response.body);           // ReadableStream
  console.log('Response bodyUsed:', response.bodyUsed);   // false (还未读取)
  
  // 使用 Body 混入的方法解析响应体
  return response.json();
})
.then(data => {
  console.log('解析后的数据:', data);
});

// 示例:Request 对象使用 Body 混入的方法
console.log('=== Request 对象的 Body 混入 ===');
let requestWithBody = new Request('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify({ title: 'test', body: 'test body', userId: 1 })
});

console.log('Request body:', requestWithBody.body);         // ReadableStream
console.log('Request bodyUsed:', requestWithBody.bodyUsed); // false (还未读取)

// 克隆并读取 Request 体
let clonedRequest = requestWithBody.clone();
clonedRequest.json().then(data => {
console.log('Request body 数据:', data);
console.log('原 Request bodyUsed:', requestWithBody.bodyUsed);   // false
console.log('克隆 Request bodyUsed:', clonedRequest.bodyUsed);   // true (已被读取)
});

// 示例:Body 混入的各种方法
console.log('=== Body 混入的各种方法 ===');

// text() 方法 - 获取文本格式
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.text())
.then(text => {
  console.log('文本格式的响应:', text.substring(0, 50) + '...');
});

// json() 方法 - 解析为 JSON
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(json => {
  console.log('JSON 格式的响应:', json);
});

// blob() 方法 - 获取 Blob 对象
fetch('https://jsonplaceholder.typicode.com/photos/1')
.then(response => response.blob())
.then(blob => {
  console.log('Blob 格式的响应:', blob);
  console.log('Blob 类型:', blob.type);
  console.log('Blob 大小:', blob.size);
})
.catch(err => {
  // 图片接口可能跨域,这里捕获错误
  console.log('获取图片 Blob 失败(可能是因为跨域):', err.message);
});

// arrayBuffer() 方法 - 获取 ArrayBuffer
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.arrayBuffer())
.then(buffer => {
  console.log('ArrayBuffer 格式的响应:', buffer);
  console.log('ArrayBuffer 长度:', buffer.byteLength);
});

// formData() 方法 - 获取 FormData(适用于表单数据)
/*
// 这个示例需要服务器返回表单格式的数据
fetch('/form-handler', {
method: 'POST',
body: new FormData(document.querySelector('form'))
})
.then(response => response.formData())
.then(formData => {
for (let [key, value] of formData.entries()) {
  console.log(key, value);
}
});
*/

// Body 只能被读取一次的特性
console.log('=== Body 只能被读取一次 ===');
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
  console.log('读取前 bodyUsed:', response.bodyUsed); // false
  
  // 第一次读取
  return response.text().then(text => {
    console.log('第一次读取结果长度:', text.length);
    console.log('第一次读取后 bodyUsed:', response.bodyUsed); // true
    
    // 尝试第二次读取会失败
    return response.text().catch(err => {
      console.log('第二次读取失败:', err.message); // "Body has already been consumed."
    });
  });
});

// 正确处理多次读取的方法 - 使用 clone()
console.log('=== 使用 clone() 方法处理多次读取 ===');
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
  // 克隆响应对象
  let clone1 = response.clone();
  let clone2 = response.clone();
  
  // 可以分别读取不同的克隆
  Promise.all([
    response.text(),      // 原始响应
    clone1.json(),        // 克隆1解析为 JSON
    clone2.blob()         // 克隆2解析为 Blob
  ]).then(([text, json, blob]) => {
    console.log('文本格式长度:', text.length);
    console.log('JSON 格式标题:', json.title);
    console.log('Blob 格式大小:', blob.size);
  });
});
相关推荐
深蓝电商API13 天前
Requests 库详解:爬虫工程师的 “瑞士军刀”
爬虫·request
Ice__Cai2 个月前
Flask 之 Request 对象详解:全面掌握请求数据处理
后端·python·flask·request·python web框架
天若有情6732 个月前
【python】Python爬虫入门教程:使用requests库
开发语言·爬虫·python·网络爬虫·request
wsdhla5 个月前
form表单提交前设置请求头request header及文件下载
html·form·request·send·header·submit
Yant2247 个月前
Django REST Framework 请求封装源码解析与实现流程
django·sqlite·drf·request·源码解析
Minner-Scrapy7 个月前
Web3.py 入门笔记
python·web3·web3.py·eth·request
老友@7 个月前
在 Spring Boot 中使用异步线程时的 HttpServletRequest 复用问题
java·spring boot·后端·tomcat·多线程·request·异步线程
码农研究僧7 个月前
UniApp 中封装 HTTP 请求与 Token 管理(附Demo)
vue3·uniapp·js·token·request
无名之逆9 个月前
搭建SSL邮件服务器
开发语言·网络·网络协议·http·https·ssl·request