JavaScript的 fetch() 方法 笔记250810
JavaScript 的 fetch() 方法是现代浏览器提供的用于发起网络请求的 API。它基于 Promise 设计,相比传统的 XMLHttpRequest 更简洁强大。以下是核心知识点和示例:
基础语法
fetch(url [, options])
.then(response => /* 处理响应 */)
.catch(error => /* 处理错误 */);
url:请求的目标地址(必需)options:配置对象(可选),包含:method:请求方法(GET,POST,PUT等),默认为GETheaders:请求头(如{ 'Content-Type': 'application/json' })body:请求体数据(POST/PUT时使用,需字符串化)credentials:是否发送 cookies(include、same-origin或omit)mode:跨域模式(cors、no-cors、same-origin)
关键特性
-
返回 Promise 对象
- 网络错误(如无法连接)会触发
catch。 - HTTP 错误状态(如 404、500)不会触发
catch,需手动检查response.ok。
- 网络错误(如无法连接)会触发
-
需解析响应体
响应对象Response提供多种解析方法:response.json()→ 解析为 JSON 对象response.text()→ 解析为文本response.blob()→ 解析为二进制 Blob
-
默认不携带 Cookies
需显式设置credentials: 'include'。
使用示例 1. GET 请求(获取 JSON 数据)
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) throw new Error('网络响应异常');
return response.json(); // 解析 JSON
})
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
2. POST 请求(提交 JSON)
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify({ name: 'Alice', age: 30 }), // 对象转 JSON 字符串
credentials: 'include' // 携带 Cookies
})
.then(response => response.json())
.then(data => console.log('创建成功:', data));
3. 错误处理(统一捕获)
async function fetchData() {
try {
const response = await fetch('https://invalid-url');
if (!response.ok) throw new Error(`HTTP 错误! 状态码: ${response.status}`);
const data = await response.json();
return data;
} catch (error) {
console.error('请求出错:', error);
}
}
4. 取消请求(使用 AbortController )
const controller = new AbortController();
const { signal } = controller;
// 设置超时取消(5秒)
setTimeout(() => controller.abort(), 5000);
fetch('https://slow-api.com/data', { signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求被取消!');
}
});
常见问题
-
跨域请求(CORS)
服务端需设置Access-Control-Allow-Origin等响应头,否则请求会被浏览器拦截。 -
HTTP 错误状态处理
务必检查response.ok或response.status,因为fetch只在网络故障时拒绝 Promise。 -
请求体格式
body必须是字符串(如JSON.stringify())、FormData或Blob对象。
兼容性
- 所有现代浏览器(Chrome 42+、Firefox 39+、Safari 10.1+)均支持。
- 旧浏览器(如 IE)需使用 polyfill(如 whatwg-fetch)。
通过 fetch(),你可以更优雅地处理网络请求,结合 async/await 语法能大幅提升代码可读性。
fetch() 函数详解
JavaScript 的 fetch() 函数详解
fetch() 是现代 JavaScript 中用于发起网络请求的核心 API,基于 Promise 设计,提供了比传统 XMLHttpRequest 更简洁强大的功能。下面从多个维度深入解析:
一、基础架构
fetch(resource [, init])
.then(response => { /* 处理响应 */ })
.catch(error => { /* 处理错误 */ });
| 参数 | 类型 | 说明 |
|---|---|---|
resource | String/Request | 请求 URL 或 Request 对象实例 |
init | Object | 可选配置对象(见下文) |
二、配置对象(init)详解
{
// 请求方法(默认:'GET')
method: 'POST',
// 请求头
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
// 请求体(GET/HEAD 请求不能包含)
body: JSON.stringify({ key: 'value' }),
// 凭证控制
credentials: 'include', // 可选值: 'omit'(默认)、'same-origin'、'include'
// 请求模式
mode: 'cors', // 可选值: 'cors'(默认)、'no-cors'、'same-origin'
// 缓存策略
cache: 'no-cache', // 可选值: 'default'、'no-store'、'reload' 等
// 重定向策略
redirect: 'follow', // 可选值: 'follow'(默认)、'error'、'manual'
// 引用策略
referrerPolicy: 'no-referrer',
// 完整性校验
integrity: 'sha256-BpfBw7ivV8q2jLiT13...',
// 信号控制(用于取消请求)
signal: abortController.signal
}
三、响应处理(Response 对象)
fetch() 返回的 Promise 解析为 Response 对象,包含以下重要属性和方法:
属性:
response.ok:布尔值,HTTP 状态码 200-299 时为 trueresponse.status:HTTP 状态码(如 200、404)response.statusText:状态文本(如 "OK")response.headers:响应头对象response.url:最终请求 URL(考虑重定向后)
解析方法:
response.json()→ 解析为 JSON 对象response.text()→ 解析为字符串response.blob()→ 解析为 Blob 对象response.arrayBuffer()→ 解析为 ArrayBufferresponse.formData()→ 解析为 FormData 对象
注意:每个解析方法只能使用一次,如需多次使用需先克隆响应:
const cloned = response.clone()
四、完整请求生命周期示例
1. GET 请求(带错误处理)
fetch('https://api.example.com/data')
.then(response => {
// 检查网络响应状态
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// 检查内容类型
const contentType = response.headers.get('content-type');
if (!contentType.includes('application/json')) {
throw new TypeError("非JSON响应");
}
return response.json();
})
.then(data => {
console.log('获取数据:', data);
})
.catch(error => {
console.error('请求失败:', error);
});
2. POST 请求(提交表单数据)
const formData = new FormData();
formData.append('username', 'john');
formData.append('avatar', fileInput.files[0]);
fetch('/api/user', {
method: 'POST',
body: formData, // 自动设置 multipart/form-data
credentials: 'include'
});
3. 带超时的请求(AbortController)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch('https://slow-api.com/data', {
signal: controller.signal
})
.then(response => response.json())
.then(data => {
clearTimeout(timeoutId);
console.log(data);
})
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求超时取消');
} else {
console.error('其他错误:', err);
}
});
五、高级应用场景
1. 并行请求(Promise.all)
const urls = ['/api/user', '/api/posts', '/api/notifications'];
Promise.all(urls.map(url =>
fetch(url).then(res => res.json())
))
.then(([user, posts, notifications]) => {
console.log('全部数据加载完成');
});
2. 流式处理(大文件处理)
fetch('https://example.com/large-video.mp4')
.then(response => {
const reader = response.body.getReader();
const stream = new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
push();
});
}
push();
}
});
return new Response(stream);
})
.then(response => response.blob())
.then(blob => {
videoElement.src = URL.createObjectURL(blob);
});
3. 自定义请求对象
const myRequest = new Request('/api/data', {
method: 'PUT',
headers: new Headers({
'X-Custom-Header': 'value'
}),
body: JSON.stringify({ update: 'new value' })
});
fetch(myRequest)
.then(/* ... */);
六、关键注意事项
-
CORS 限制
跨域请求需要服务器设置正确的响应头(如Access-Control-Allow-Origin) -
Cookie 策略
默认不发送/接收 cookies,需显式设置credentials: 'include' -
错误处理差异
- 网络故障:触发
catch或 Promise 拒绝 - HTTP 错误(4xx/5xx):需手动检查
response.ok
- 网络故障:触发
-
数据格式处理
- JSON 数据需使用
JSON.stringify()转换 - FormData 会自动设置正确的 Content-Type
- JSON 数据需使用
-
性能优化
- 使用
AbortController实现请求超时和取消 - 大文件处理优先使用流式 API
- 使用
七、浏览器兼容性
| 浏览器 | 支持版本 | 备注 |
|---|---|---|
| Chrome | 42+ | 完整支持 |
| Firefox | 39+ | 完整支持 |
| Safari | 10.1+ | 部分高级特性需更新版本 |
| Edge | 14+ | 完整支持 |
| Internet Explorer | 不支持 | 需使用 polyfill |
Polyfill 方案:
<!-- 通过 npm 安装 -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>
八、最佳实践总结
- 始终检查
response.ok或response.status - 使用
async/await提升代码可读性 - 为敏感请求设置超时限制(AbortController)
- 根据响应内容类型选择正确的解析方法
- 跨域请求确保服务器配置 CORS 头部
- 需要 cookies 认证时设置
credentials: 'include'
通过掌握这些核心概念,您可以高效安全地使用 fetch() 处理各种网络请求场景。
JavaScript Fetch API 全面指南
Fetch API 是现代 JavaScript 中用于网络请求的核心技术,它基于 Promise 设计,提供了比传统 XMLHttpRequest 更强大、更灵活的解决方案。本指南将深入探讨 Fetch API 的各个方面。
一、Fetch API 核心概念
1. 基本架构
fetch(resource [, init])
.then(response => { /* 处理响应 */ })
.catch(error => { /* 处理错误 */ });
- resource:请求资源(URL 字符串或 Request 对象)
- init:配置对象(可选)
2. 与传统 XHR 的对比
| 特性 | Fetch API | XMLHttpRequest |
|---|---|---|
| 语法 | Promise-based | 回调函数 |
| 请求取消 | AbortController | xhr.abort() |
| 流式处理 | 原生支持 | 有限支持 |
| CORS 处理 | 更简洁 | 较复杂 |
| 请求体/响应体类型 | 丰富(Blob、FormData等) | 有限 |
二、请求配置详解
1. 完整配置选项
{
method: 'POST', // HTTP 方法
headers: { // 请求头
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify(data), // 请求体
mode: 'cors', // 跨域模式
cache: 'no-cache', // 缓存策略
credentials: 'include', // 凭证控制
redirect: 'follow', // 重定向策略
referrerPolicy: 'no-referrer', // 来源策略
integrity: 'sha256-...', // 子资源完整性
signal: abortSignal // 中止信号
}
2. 请求体支持类型
- JSON 数据:
JSON.stringify({ key: 'value' }) - FormData:
new FormData(formElement) - URLSearchParams:
new URLSearchParams({ key: 'value' }) - Blob/BufferSource:二进制数据
- 字符串:普通文本
三、响应处理深度解析
1. Response 对象结构
{
ok: true, // 状态码 200-299 时为 true
status: 200, // HTTP 状态码
statusText: 'OK', // 状态文本
headers: Headers {}, // 响应头对象
url: 'https://...', // 最终 URL(含重定向)
type: 'cors', // 响应类型
redirected: false, // 是否重定向
body: ReadableStream, // 可读流
bodyUsed: false // 是否已读取
}
2. 响应数据解析方法
// JSON 数据
const data = await response.json();
// 文本内容
const text = await response.text();
// Blob 对象(如图片)
const blob = await response.blob();
// ArrayBuffer(二进制数据)
const buffer = await response.arrayBuffer();
// FormData(表单数据)
const formData = await response.formData();
重要:每个解析方法只能调用一次,如需多次使用需先克隆响应:
const clone1 = response.clone(); const clone2 = response.clone(); const data1 = await clone1.json(); const data2 = await clone2.text();
四、高级应用场景
1. 请求取消(AbortController)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
// 处理响应...
} catch (err) {
if (err.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('请求错误:', err);
}
}
2. 流式数据处理
const response = await fetch('https://example.com/large-file');
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('接收到数据块:', value);
// 处理数据块(如实时渲染)
}
3. 上传进度监控
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`上传进度: ${percent}%`);
};
// 使用 Fetch + Progress (实验性)
const response = await fetch(url, {
method: 'POST',
body: file,
duplex: 'half' // 必需
});
// 使用 ReadableStream 模拟进度
const progressStream = new TransformStream({
transform(chunk, controller) {
loaded += chunk.byteLength;
updateProgress(loaded, total);
controller.enqueue(chunk);
}
});
4. 自定义请求拦截
// 全局请求拦截
const originalFetch = window.fetch;
window.fetch = async (resource, init) => {
// 请求前处理(如添加认证头)
init.headers = {
...init.headers,
'X-Auth-Token': getToken()
};
const start = Date.now();
const response = await originalFetch(resource, init);
const duration = Date.now() - start;
// 响应后处理(如日志记录)
logRequest(resource, response.status, duration);
return response;
};
五、错误处理最佳实践
1. 综合错误处理方案
async function safeFetch(url, options) {
try {
const response = await fetch(url, options);
// 处理 HTTP 错误状态
if (!response.ok) {
const error = new Error(`HTTP 错误! 状态码: ${response.status}`);
error.status = response.status;
throw error;
}
// 根据内容类型解析
const contentType = response.headers.get('content-type');
if (contentType.includes('application/json')) {
return await response.json();
} else if (contentType.includes('text/')) {
return await response.text();
} else {
return await response.blob();
}
} catch (error) {
// 分类处理不同错误类型
if (error.name === 'AbortError') {
console.warn('请求被取消');
} else if (error.name === 'TypeError') {
console.error('网络错误或 CORS 问题');
} else {
console.error('请求失败:', error);
}
// 返回统一错误格式
return {
success: false,
error: error.message,
status: error.status || 0
};
}
}
六、性能优化技巧
-
请求合并:
// 并行请求 const [user, posts] = await Promise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/posts').then(r => r.json()) ]); -
缓存策略:
fetch(url, { cache: 'force-cache' // 优先使用缓存 }); // 或者 fetch(url, { cache: 'reload' // 绕过缓存 }); -
数据压缩:
fetch(url, { headers: { 'Accept-Encoding': 'gzip, deflate, br' } }); -
HTTP/2 服务端推送:
// 服务器端配置 Link: </styles.css>; rel=preload; as=style
七、安全实践
-
CSRF 防护:
fetch(url, { headers: { 'X-CSRF-Token': getCSRFToken() } }); -
内容安全策略(CSP):
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'"> -
子资源完整性(SRI):
fetch('https://cdn.example.com/library.js', { integrity: 'sha384-...' }); -
CORS 安全配置:
// 服务器端响应头 Access-Control-Allow-Origin: https://yourdomain.com Access-Control-Allow-Credentials: true
八、浏览器兼容性与 Polyfill
| 浏览器 | 支持版本 | 注意事项 |
|---|---|---|
| Chrome | 42+ | 完整支持 |
| Firefox | 39+ | 完整支持 |
| Safari | 10.1+ | 部分高级特性需更新版本 |
| Edge | 14+ | 完整支持 |
| IE | 不支持 | 需使用 polyfill |
Polyfill 方案:
<!-- 使用 whatwg-fetch polyfill -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>
<!-- 或通过 npm 安装 -->
npm install whatwg-fetch
// 在应用入口引入
import 'whatwg-fetch';
九、实际应用示例
1. 文件分片上传
async function uploadFile(file) {
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let i = 0; i < totalChunks; i++) {
const chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', totalChunks);
formData.append('fileName', file.name);
await fetch('/api/upload', {
method: 'POST',
body: formData
});
console.log(`上传分片 ${i + 1}/${totalChunks}`);
}
// 通知服务器合并文件
await fetch('/api/merge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileName: file.name })
});
}
2. 带重试机制的请求
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
// 服务器错误时重试
if (response.status >= 500) {
throw new Error(`服务器错误: ${response.status}`);
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
// 指数退避重试
const delay = Math.pow(2, i) * 1000 + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
十、未来发展趋势
-
Fetch 元数据:
// 实验性功能 fetch(url, { priority: 'high' // 请求优先级 }); -
请求诊断 API:
const resourceTiming = performance.getEntriesByName(url)[0]; console.log('DNS 时间:', resourceTiming.domainLookupEnd - resourceTiming.domainLookupStart); -
WebTransport:
// 下一代传输协议(基于 HTTP/3) const transport = new WebTransport('https://example.com'); const stream = await transport.createBidirectionalStream(); -
后台同步 API:
// 服务工作者中使用 self.registration.sync.register('sync-data');
总结
Fetch API 是现代 Web 开发的基石,提供了强大而灵活的网络请求能力。关键要点:
- 始终检查
response.ok处理 HTTP 错误 - 使用
AbortController实现请求取消和超时 - 根据内容类型选择合适的解析方法
- 重要请求实现重试和回退机制
- 敏感请求添加 CSRF 保护
- 大文件处理使用流式 API
- 兼容旧浏览器使用 polyfill
随着 Web 平台的发展,Fetch API 将继续演进,为开发者提供更强大的网络能力。掌握这些技术将使您能够构建更健壮、更高效的 Web 应用。
fetch 的配置项
Fetch API 配置项全面指南
Fetch API 的配置项是控制 HTTP 请求行为的核心参数,通过 fetch(url, options) 的第二个参数传递。以下是所有可用配置项的详细说明和示例:
一、基础配置项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
method | String | 'GET' | HTTP 方法 (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
headers | Object | {} | 请求头对象 |
body | Various | null | 请求体数据(Blob, BufferSource, FormData, URLSearchParams, String) |
mode | String | 'cors' | 请求模式:'cors', 'no-cors', 'same-origin', 'navigate' |
credentials | String | 'same-origin' | 凭证控制:'omit', 'same-origin', 'include' |
cache | String | 'default' | 缓存策略:'default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached' |
redirect | String | 'follow' | 重定向策略:'follow', 'error', 'manual' |
referrer | String | 'about:client' | 来源页面 URL |
referrerPolicy | String | 'no-referrer-when-downgrade' | 来源策略(控制 Referer 头) |
integrity | String | '' | 子资源完整性校验(如 'sha256-BpfBw7ivV8q2jLiT13...') |
signal | AbortSignal | null | 用于取消请求的信号对象 |
priority | String | 'auto' | 请求优先级:'high', 'low', 'auto'(实验性功能) |
duplex | String | - | 双工模式:'half'(实验性,用于支持进度监控) |
二、配置项详解与示例
1. 基础请求配置
// POST 请求示例
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ key: 'value' })
});
2. 跨域与凭证控制
// 跨域请求带凭证
fetch('https://cross-origin.com/api', {
mode: 'cors', // 必需跨域模式
credentials: 'include', // 包含 cookies
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
3. 缓存控制策略
// 强制缓存验证
fetch('/static/data.json', {
cache: 'no-cache' // 使用缓存但验证新鲜度
});
// 完全绕过缓存
fetch('/latest-data', {
cache: 'reload' // 忽略缓存,直接从服务器获取
});
// 仅使用缓存
fetch('/cached-data', {
cache: 'only-if-cached' // 仅从缓存读取,不发送请求
});
4. 重定向处理
// 禁止重定向
fetch('/redirect-endpoint', {
redirect: 'error' // 遇到重定向时抛出错误
});
// 手动处理重定向
fetch('/redirect', {
redirect: 'manual'
}).then(response => {
if (response.type === 'opaqueredirect') {
// 手动处理重定向
window.location = response.url;
}
});
5. 请求完整性校验
// 确保资源未被篡改
fetch('/critical-script.js', {
integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC'
});
6. 取消请求(AbortController)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch('/slow-api', {
signal: controller.signal
})
.then(response => {
clearTimeout(timeoutId);
return response.json();
})
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求超时取消');
}
});
7. 请求优先级(实验性)
// 高优先级请求(如图片/关键数据)
fetch('/hero-image.jpg', {
priority: 'high'
});
// 低优先级请求(如日志、分析)
fetch('/analytics', {
priority: 'low'
});
三、body 数据类型处理
1. 不同数据类型的 body 配置
| 数据类型 | Content-Type 头 | 示例 |
|---|---|---|
| JSON 对象 | application/json | body: JSON.stringify({key: 'value'}) |
| FormData | multipart/form-data | body: new FormData(formElement) |
| URLSearchParams | application/x-www-form-urlencoded | body: new URLSearchParams({key: 'value'}) |
| Blob/File | 自动设置 | body: new Blob([JSON.stringify(data)]) |
| ArrayBuffer | 根据内容设置 | body: new Uint8Array([1,2,3]).buffer |
| 字符串 | text/plain | body: 'plain text' |
2. 完整示例
// FormData 上传文件
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('comment', '文件说明');
fetch('/upload', {
method: 'POST',
body: formData // 自动设置 multipart/form-data
});
// URLSearchParams 提交表单
const params = new URLSearchParams();
params.append('search', 'JavaScript');
params.append('page', '1');
fetch('/search', {
method: 'POST',
body: params // 自动设置 application/x-www-form-urlencoded
});
// Blob 上传
const blob = new Blob([JSON.stringify({ data: 'value' })], {
type: 'application/json'
});
fetch('/submit', {
method: 'POST',
body: blob
});
四、headers 配置详解
1. 设置请求头
// 使用 Headers 对象
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('X-Custom-Header', 'value');
// 或使用普通对象
fetch('/api', {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Request-ID': uuidv4()
}
});
2. 禁止的请求头 以下请求头由浏览器控制,无法通过 JavaScript 设置:
Accept-CharsetAccept-EncodingAccess-Control-Request-HeadersAccess-Control-Request-MethodConnectionContent-LengthCookieCookie2DateDNTExpectHostKeep-AliveOriginRefererTETrailerTransfer-EncodingUpgradeVia
五、高级配置场景
1. 流式上传(带进度)
// 创建带进度监控的可读流
function createProgressStream(stream, onProgress) {
let loaded = 0;
return new ReadableStream({
start(controller) {
const reader = stream.getReader();
async function push() {
const { done, value } = await reader.read();
if (done) {
controller.close();
return;
}
loaded += value.length;
onProgress(loaded);
controller.enqueue(value);
push();
}
push();
}
});
}
const fileStream = file.stream();
const progressStream = createProgressStream(fileStream, loaded => {
console.log(`已上传: ${loaded} 字节`);
});
fetch('/upload', {
method: 'POST',
headers: { 'Content-Type': file.type },
body: progressStream,
duplex: 'half' // 必需参数
});
2. 服务端事件 (SSE) 替代方案
// 使用 Fetch 实现类 SSE 功能
async function streamEvents(url) {
const response = await fetch(url, {
headers: { 'Accept': 'text/event-stream' }
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
chunk.split('\n\n').forEach(event => {
if (event.trim()) {
const [id, type, data] = parseEvent(event);
handleEvent({ id, type, data });
}
});
}
}
3. HTTP/2 服务器推送
// 利用服务器推送(需要服务端支持)
fetch('/main-resource', {
headers: { 'Accept': 'multipart/mixed' }
}).then(response => {
// 处理主资源和推送资源
const reader = response.body.getReader();
// ...解析多部分响应...
});
六、配置项最佳实践
-
始终设置超时:
function fetchWithTimeout(url, options, timeout = 8000) { const controller = new AbortController(); setTimeout(() => controller.abort(), timeout); return fetch(url, { ...options, signal: controller.signal }); } -
JSON 请求封装:
async function jsonFetch(url, method = 'GET', data = null) { const options = { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include' }; if (data) options.body = JSON.stringify(data); const response = await fetch(url, options); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); } -
安全请求配置:
fetch('/sensitive', { method: 'POST', credentials: 'include', integrity: 'sha256-...', // 完整性校验 headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCSRFToken(), // CSRF 防护 'Strict-Transport-Security': 'max-age=31536000' // HSTS } });
七、浏览器兼容性注意事项
-
实验性功能支持:
priority:仅 Chrome 65+ 支持duplex:仅 Chrome 105+ 支持integrity:所有现代浏览器支持
-
Polyfill 方案:
<!-- 基础 Fetch polyfill --> <script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script> <!-- AbortController polyfill --> <script src="https://cdn.jsdelivr.net/npm/abortcontroller-polyfill@1.7.3/dist/abortcontroller-polyfill-only.min.js"></script> -
Node.js 支持:
// Node.js 18+ 实验性支持 const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); // 或使用 node-fetch 库 const fetch = require('node-fetch');
八、常见问题解决方案
-
CORS 问题:
fetch('https://cross-origin.com/api', { mode: 'cors', headers: { // 服务器需要配置 Access-Control-Allow-Origin } }); -
Cookie 未发送:
fetch('/auth', { credentials: 'include' // 必需 }); -
多次读取响应体:
fetch('/data') .then(response => { const clone = response.clone(); // 克隆响应 return Promise.all([ response.json(), clone.text() ]); }); -
上传进度监控:
// 使用 XMLHttpRequest 替代或实验性 Streams API
掌握 Fetch API 的配置项是优化网络请求的关键,合理组合这些选项可以实现从简单请求到复杂场景的全方位控制。根据目标浏览器环境选择合适的配置,并始终考虑安全性和错误处理,将大大提升应用的网络请求质量。
fetch 的 Response
Response 对象详解
在 JavaScript Fetch API 中,Response 对象代表了对 HTTP 请求的响应。它包含了服务器返回的所有信息,并提供了多种方法来处理响应数据。以下是 Response 对象的全面解析:
一、Response 对象属性详解
| 属性 | 类型 | 说明 |
|---|---|---|
ok | Boolean | 状态码在 200-299 范围内时为 true |
status | Number | HTTP 状态码(如 200, 404, 500 等) |
statusText | String | 状态文本(如 "OK", "Not Found") |
url | String | 最终响应 URL(考虑重定向后的地址) |
headers | Headers 对象 | 响应头信息 |
type | String | 响应类型(basic, cors, error, opaque, opaqueredirect) |
redirected | Boolean | 是否经过重定向 |
body | ReadableStream | 可读流形式的响应体 |
bodyUsed | Boolean | 是否已读取过响应体 |
示例:访问属性
fetch('https://api.example.com/data')
.then(response => {
console.log('状态码:', response.status);
console.log('状态文本:', response.statusText);
console.log('内容类型:', response.headers.get('content-type'));
console.log('响应URL:', response.url);
console.log('是否成功:', response.ok);
});
二、响应体处理方法
Response 对象提供多种方法解析响应体,每个方法只能调用一次:
1. response.json() 将响应体解析为 JSON 对象
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
2. response.text() 获取响应体文本内容
fetch('/text-file.txt')
.then(response => response.text())
.then(text => console.log(text));
3. response.blob() 获取二进制 Blob 对象(适合文件下载)
fetch('/image.png')
.then(response => response.blob())
.then(blob => {
const img = new Image();
img.src = URL.createObjectURL(blob);
document.body.appendChild(img);
});
4. response.arrayBuffer() 获取原始二进制 ArrayBuffer(适合音频/视频处理)
fetch('/audio.mp3')
.then(response => response.arrayBuffer())
.then(buffer => {
const audioCtx = new AudioContext();
audioCtx.decodeAudioData(buffer, audio => {
// 处理音频数据
});
});
5. response.formData() 解析表单数据
fetch('/form-submit')
.then(response => response.formData())
.then(formData => {
console.log(formData.get('username'));
});
三、Headers 对象操作
response.headers 提供对响应头的访问:
// 获取单个头信息
const contentType = response.headers.get('content-type');
// 检查头是否存在
if (response.headers.has('X-Custom-Header')) {
// ...
}
// 遍历所有头信息
response.headers.forEach((value, name) => {
console.log(`${name}: ${value}`);
});
// 转为普通对象
const headersObj = Object.fromEntries(response.headers.entries());
四、克隆响应对象
当需要多次使用响应体时(如同时获取 JSON 和原始文本),需先克隆响应:
fetch('/api/data')
.then(response => {
// 创建两个克隆
const jsonClone = response.clone();
const textClone = response.clone();
return Promise.all([
jsonClone.json(),
textClone.text()
]);
})
.then(([data, rawText]) => {
console.log('Parsed data:', data);
console.log('Raw text:', rawText);
});
五、响应类型(Response.type)
| 类型 | 说明 |
|---|---|
basic | 同源响应 |
cors | 有效的跨域响应(包含 CORS 头) |
error | 网络错误(无法完成请求) |
opaque | 跨域请求但未返回 CORS 头(无法读取状态/头信息) |
opaqueredirect | 重定向模式设为 "manual" 时的重定向响应 |
检测响应类型:
fetch('https://cross-origin.com/data')
.then(response => {
if (response.type === 'cors') {
console.log('有效的跨域响应');
} else if (response.type === 'opaque') {
console.log('受限的跨域响应');
}
});
六、自定义 Response 对象
可手动创建 Response 对象用于测试或 Service Worker:
// 创建简单文本响应
const textResponse = new Response('Hello, world!', {
status: 200,
headers: new Headers({
'Content-Type': 'text/plain'
})
});
// 创建 JSON 响应
const jsonResponse = new Response(JSON.stringify({ data: 123 }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
// 创建错误响应
const errorResponse = Response.error(); // status=0, type=error
七、流式处理响应体
处理大文件时使用流式 API:
fetch('/large-video.mp4')
.then(response => {
const reader = response.body.getReader();
const stream = new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
push();
});
}
push();
}
});
return new Response(stream);
})
.then(response => response.blob())
.then(blob => {
// 处理大文件
});
八、在 Service Worker 中的高级用法
1. 缓存响应
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request))
);
});
2. 合成响应
self.addEventListener('fetch', event => {
if (event.request.url.endsWith('/custom-data')) {
const data = { timestamp: Date.now() };
const jsonResponse = new Response(
JSON.stringify(data),
{ headers: { 'Content-Type': 'application/json' } }
);
event.respondWith(jsonResponse);
}
});
九、注意事项与最佳实践
-
响应体单次读取:
// 错误示例:尝试多次读取 fetch('/data') .then(response => { const data = response.json(); // 第一次读取 const text = response.text(); // 错误!bodyUsed 已为 true }); -
HTTP 错误处理:
fetch('/api') .then(response => { if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return response.json(); }); -
内容类型验证:
fetch('/data') .then(response => { const contentType = response.headers.get('content-type'); if (!contentType.includes('application/json')) { throw new TypeError("Expected JSON response"); } return response.json(); }); -
超时控制:
const controller = new AbortController(); setTimeout(() => controller.abort(), 5000); fetch('/slow-api', { signal: controller.signal }) .then(response => response.json()) .catch(err => { if (err.name === 'AbortError') { console.log('Request timed out'); } });
十、浏览器兼容性
| 浏览器 | 支持版本 | 备注 |
|---|---|---|
| Chrome | 42+ | 完整支持 |
| Firefox | 39+ | 完整支持 |
| Safari | 10.1+ | 流式处理需更新版本 |
| Edge | 14+ | 完整支持 |
| Node.js | 18+ | 实验性支持(需启用 flag) |
Polyfill 方案:
npm install node-fetch # Node.js 环境
npm install whatwg-fetch # 浏览器环境
总结
Response 对象是 Fetch API 的核心组件,提供了:
- 访问 HTTP 响应元数据(状态码、头信息等)
- 多种响应体解析方法(JSON、文本、Blob 等)
- 流式处理能力(适合大文件)
- 克隆机制(允许多次使用响应)
- 自定义响应创建能力
掌握 Response 对象的正确使用方式,结合适当的错误处理和流控制,可以构建高效可靠的网络请求处理逻辑,特别是在处理大型数据集或媒体文件时表现出色。
XMLHttpRequest
XMLHttpRequest 全面解析
XMLHttpRequest (XHR) 是浏览器提供的 JavaScript API,用于在客户端和服务器之间传输数据。它是 AJAX (Asynchronous JavaScript and XML) 技术的核心基础,虽然现在有更现代的 Fetch API,但 XHR 仍然广泛使用且功能强大。
一、基础架构与核心概念
1. 创建实例
const xhr = new XMLHttpRequest();
2. 请求生命周期
- 创建对象:
new XMLHttpRequest() - 配置请求:
.open(method, url, async) - 设置回调:
.onreadystatechange - 发送请求:
.send(body) - 处理响应:检查
readyState和status
二、核心方法与属性
1. 请求配置方法
// 初始化请求
xhr.open(method, url, async, user, password);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer token');
// 设置响应类型
xhr.responseType = 'json'; // 可选: 'text', 'arraybuffer', 'blob', 'document'
2. 状态属性
// 请求状态 (0-4)
xhr.readyState;
// HTTP 状态码
xhr.status;
// 状态文本
xhr.statusText;
// 响应数据
xhr.response;
xhr.responseText; // 当 responseType 为 'text' 时
xhr.responseXML; // 当响应是 XML 时
3. readyState 状态详解
| 值 | 状态 | 描述 |
|---|---|---|
| 0 | UNSENT | 对象已创建,open() 未调用 |
| 1 | OPENED | open() 已调用 |
| 2 | HEADERS_RECEIVED | 收到响应头 |
| 3 | LOADING | 接收响应体中 |
| 4 | DONE | 请求完成(成功或失败) |
三、完整请求示例
1. GET 请求
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log('成功:', JSON.parse(xhr.responseText));
} else {
console.error('错误:', xhr.statusText);
}
}
};
xhr.onerror = function() {
console.error('网络错误');
};
xhr.send();
2. POST 请求 (JSON)
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/submit', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('提交成功:', xhr.response);
}
};
const data = JSON.stringify({ name: 'John', age: 30 });
xhr.send(data);
四、高级功能
1. 超时处理
xhr.timeout = 5000; // 5秒超时
xhr.ontimeout = function() {
console.error('请求超时');
};
2. 进度监控
// 上传进度
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
console.log(`上传: ${percent.toFixed(2)}%`);
}
};
// 下载进度
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
console.log(`下载: ${percent.toFixed(2)}%`);
}
};
3. 中止请求
const xhr = new XMLHttpRequest();
// ...配置请求...
// 中止请求
document.getElementById('cancelBtn').addEventListener('click', () => {
xhr.abort();
console.log('请求已取消');
});
4. 文件上传
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('username', 'john_doe');
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.upload.onprogress = function(e) {
// 更新进度条
};
xhr.onload = function() {
if (xhr.status === 200) {
console.log('上传成功');
}
};
xhr.send(formData);
五、事件处理模型
| 事件 | 触发时机 |
|---|---|
onreadystatechange | readyState 变化时触发 |
onloadstart | 请求开始时触发 |
onprogress | 数据传输过程中定期触发 |
onabort | 请求被中止时触发 |
onerror | 请求失败时触发 |
onload | 请求成功完成时触发 |
ontimeout | 请求超时时触发 |
onloadend | 请求结束时触发(无论成功或失败) |
xhr.addEventListener('load', function() {
console.log('请求完成');
});
xhr.addEventListener('error', function() {
console.error('请求出错');
});
xhr.addEventListener('abort', function() {
console.warn('请求被取消');
});
六、与 Fetch API 的对比
| 特性 | XMLHttpRequest | Fetch API |
|---|---|---|
| 基本架构 | 基于事件回调 | 基于 Promise |
| 请求取消 | 原生支持 (xhr.abort()) | 需使用 AbortController |
| 进度监控 | 原生支持 (upload/onprogress) | 实验性支持或需自定义实现 |
| 超时控制 | 原生支持 (timeout 属性) | 需手动实现 |
| 响应类型 | 多种 (text, arraybuffer, blob 等) | 类似,但通过不同方法访问 |
| CORS 处理 | 需要额外处理 | 更简洁的 CORS 支持 |
| 同步请求 | 支持 (open 第三个参数设为 false) | 不支持 |
| 浏览器支持 | 所有现代浏览器 + IE7+ | 现代浏览器 (IE 不支持) |
| 请求体格式 | 支持多种 (FormData, Blob, 字符串) | 类似 |
七、跨域请求处理 (CORS)
1. 简单请求
xhr.open('GET', 'https://cross-origin.com/data', true);
xhr.send();
2. 预检请求 (Preflight)
xhr.open('PUT', 'https://cross-origin.com/update', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send(JSON.stringify({ data: 'test' }));
3. 带凭证的请求
xhr.withCredentials = true; // 发送 cookies
八、最佳实践与错误处理
1. 封装成 Promise
function xhrRequest(method, url, data = null) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
resolve(xhr.responseText);
}
} else {
reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));
}
};
xhr.onerror = () => reject(new Error('网络错误'));
xhr.ontimeout = () => reject(new Error('请求超时'));
xhr.send(data ? JSON.stringify(data) : null);
});
}
// 使用示例
xhrRequest('GET', '/api/data')
.then(data => console.log(data))
.catch(error => console.error(error));
2. 统一错误处理
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
switch (true) {
case xhr.status === 0:
console.error('请求被取消或网络错误');
break;
case xhr.status >= 200 && xhr.status < 300:
handleSuccess(xhr.response);
break;
case xhr.status === 401:
console.error('未授权,需要登录');
break;
case xhr.status === 403:
console.error('禁止访问');
break;
case xhr.status === 404:
console.error('资源不存在');
break;
case xhr.status >= 500:
console.error('服务器错误');
break;
default:
console.error(`未知错误: ${xhr.status}`);
}
}
};
九、现代应用场景
1. 大文件分片上传
function uploadFile(file) {
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', totalChunks);
formData.append('fileName', file.name);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload-chunk', true);
xhr.upload.onprogress = function(e) {
const chunkProgress = (e.loaded / e.total) * 100;
const totalProgress = ((i * CHUNK_SIZE) + e.loaded) / file.size * 100;
updateProgress(totalProgress);
};
xhr.send(formData);
}
}
2. 长轮询 (Long Polling)
function longPoll() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/updates', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
processUpdates(JSON.parse(xhr.responseText));
}
// 无论成功或失败,重新发起请求
setTimeout(longPoll, 1000);
}
};
xhr.send();
}
// 启动长轮询
longPoll();
十、兼容性与 Polyfill
1. 浏览器支持
- 所有现代浏览器完全支持
- Internet Explorer 7+
- 移动端浏览器全面支持
2. ActiveXObject (IE6及以下)
let xhr;
if (typeof XMLHttpRequest !== 'undefined') {
xhr = new XMLHttpRequest();
} else {
try {
// 旧版IE
xhr = new ActiveXObject('Msxml2.XMLHTTP.6.0');
} catch (e) {
try {
xhr = new ActiveXObject('Msxml2.XMLHTTP.3.0');
} catch (e) {
throw new Error('XMLHttpRequest not supported');
}
}
}
十一、安全注意事项
-
CSRF 防护:
// 添加CSRF令牌 const csrfToken = document.querySelector('meta[name="csrf-token"]').content; xhr.setRequestHeader('X-CSRF-Token', csrfToken); -
内容安全策略:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';"> -
输入验证:
// 验证服务器响应 if (xhr.responseType === 'document') { const doc = xhr.responseXML; // 防止XXE攻击 if (doc.documentElement.nodeName === 'parsererror') { throw new Error('无效的XML响应'); } }
总结
XMLHttpRequest 的核心优势:
- 广泛的浏览器支持(包括旧版IE)
- 完善的进度监控(上传/下载)
- 请求取消和超时的原生支持
- 同步请求能力(特定场景需要)
- 成熟的错误处理机制
虽然 Fetch API 提供了更现代的 Promise-based 接口,但 XHR 在以下场景仍不可替代:
- 需要精细控制上传/下载进度
- 兼容旧版浏览器
- 需要同步请求的特殊场景
- 现有项目中使用 XHR 的代码库
理解 XMLHttpRequest 的工作原理和高级用法,对于处理复杂网络请求、维护遗留系统和理解 AJAX 技术演进都至关重要。在现代开发中,可根据具体需求选择 XHR 或 Fetch API,甚至结合两者优势使用。
XMLHttpRequest 与 fetch
XMLHttpRequest 与 Fetch API:全面比较与深度解析
在现代 Web 开发中,XMLHttpRequest (XHR) 和 Fetch API 是两种主要的 HTTP 请求技术。本文将深入比较它们的差异、优缺点和适用场景。
一、核心架构差异
| 特性 | XMLHttpRequest (XHR) | Fetch API |
|---|---|---|
| 设计范式 | 基于事件回调 | 基于 Promise |
| 创建时间 | 1999年 (IE5) | 2015年 (ES6) |
| 标准类型 | 浏览器厂商实现 | W3C 标准 |
| 基本用法 | const xhr = new XMLHttpRequest() | fetch(url, options) |
二、功能特性对比
1. 请求与响应处理
// XHR 示例
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onload = () => {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};
xhr.send();
// Fetch 示例
fetch('/api/data')
.then(response => {
if (response.ok) return response.json();
throw new Error('请求失败');
})
.then(data => console.log(data));
2. 进度监控能力
// XHR - 原生支持进度监控
xhr.upload.onprogress = e => {
const percent = Math.round((e.loaded / e.total) * 100);
console.log(`上传进度: ${percent}%`);
};
// Fetch - 需要额外实现
const response = await fetch(url, {
method: 'POST',
body: readableStreamWithProgress(file) // 自定义进度流
});
3. 超时与取消控制
// XHR - 原生支持
xhr.timeout = 5000; // 5秒超时
xhr.ontimeout = () => console.log('请求超时');
xhr.abort(); // 取消请求
// Fetch - 需要 AbortController
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
fetch(url, {
signal: controller.signal
}).catch(e => {
if (e.name === 'AbortError') console.log('请求取消');
});
三、关键技术差异详解
1. 错误处理机制
// XHR 需要手动检查状态码
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功处理
} else {
// 错误处理
}
}
};
// Fetch 不会拒绝HTTP错误(如404)
fetch(url)
.then(response => {
if (!response.ok) { // 必须手动检查
throw new Error(`HTTP错误! 状态码: ${response.status}`);
}
return response.json();
})
.catch(error => {
// 捕获网络错误和手动抛出的错误
});
2. 请求体与响应体处理
| 数据类型 | XHR 支持 | Fetch 支持 |
|---|---|---|
| JSON | xhr.responseText | response.json() |
| 文本 | xhr.responseText | response.text() |
| Blob | xhr.response | response.blob() |
| ArrayBuffer | xhr.response | response.arrayBuffer() |
| FormData | 原生支持 | 原生支持 |
| 流数据 | 有限支持 | 原生支持(ReadableStream) |
3. CORS 与凭证管理
// XHR
xhr.withCredentials = true; // 发送凭据
// Fetch
fetch(url, {
credentials: 'include' // 包含凭据
});
四、性能与能力对比
| 能力维度 | XHR | Fetch |
|---|---|---|
| 同步请求 | ✅ 支持 | ❌ 不支持 |
| 进度事件 | ✅ 原生支持 | ⚠️ 需要自定义实现 |
| 超时控制 | ✅ 原生支持 | ⚠️ 需要 AbortController + setTimeout |
| 请求取消 | ✅ xhr.abort() | ✅ AbortController.abort() |
| 流式处理 | ❌ 有限支持 | ✅ 原生支持(ReadableStream) |
| 服务端事件(SSE) | ✅ 支持 | ❌ 不支持 |
| HTTP/2 支持 | ✅ 完全支持 | ✅ 完全支持 |
五、实际应用场景对比
1. 文件上传(带进度条)
// XHR 实现 (推荐)
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = e => updateProgress(e);
xhr.open('POST', '/upload');
xhr.send(formData);
// Fetch 实现(复杂)
// 需要创建自定义可读流实现进度跟踪
2. 大数据流处理
// XHR 有限支持
xhr.responseType = 'arraybuffer';
// Fetch 原生流支持(推荐)
fetch('/large-data')
.then(response => {
const reader = response.body.getReader();
// 流式处理数据
});
3. 简单 API 请求
// Fetch 更简洁(推荐)
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data));
// XHR 实现
const xhr = new XMLHttpRequest();
// ...需要多行代码实现相同功能
六、浏览器兼容性分析
| 浏览器 | XHR 支持 | Fetch 支持 |
|---|---|---|
| Chrome | ✅ 全部版本 | ✅ 42+ |
| Firefox | ✅ 全部版本 | ✅ 39+ |
| Safari | ✅ 全部版本 | ✅ 10.1+ |
| Edge | ✅ 全部版本 | ✅ 14+ |
| Internet Explorer | ✅ 7+ | ❌ 不支持 |
| Node.js | ❌ 不支持 | ✅ 18+ (实验性) |
Polyfill 方案:
<!-- XHR for IE5-6 -->
<script>
// 使用 ActiveXObject 实现
</script>
<!-- Fetch polyfill -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>
七、最佳实践指南
1. 何时选择 XHR
- 需要精确的上传/下载进度监控
- 兼容旧版浏览器(特别是 IE9-11)
- 实现服务器发送事件(SSE)
- 需要同步请求的特殊场景
2. 何时选择 Fetch
- 现代浏览器环境开发
- 需要流式数据处理
- 集成 Service Workers
- 需要更简洁的 Promise 链式调用
- 使用 HTTP/2 高级特性
3. 混合使用策略
// 根据需求选择最佳方案
function httpRequest(url, options = {}) {
if (options.needProgress && 'XMLHttpRequest' in window) {
return xhrRequest(url, options); // 使用XHR
}
if ('fetch' in window) {
return fetchRequest(url, options); // 使用Fetch
}
// 降级方案
return legacyXhrRequest(url, options);
}
八、未来发展趋势
-
Fetch 的持续增强
- 规范中添加了
priority属性控制请求优先级
fetch(url, { priority: 'high' }); // 实验性功能 - 规范中添加了
-
HTTP/3 支持
- Fetch API 更易适配新协议
-
WebTransport 协议
// 下一代传输协议 const transport = new WebTransport('https://example.com'); const stream = await transport.createBidirectionalStream(); -
Service Worker 集成
- Fetch 在 PWA 中无缝集成
self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) || fetch(event.request) ); });
总结:核心选择建议
| 考量因素 | 推荐选择 |
|---|---|
| 现代浏览器项目 | ✅ Fetch (首选) |
| 需要进度监控 | ✅ XHR |
| 兼容旧版浏览器 | ✅ XHR |
| 流数据处理 | ✅ Fetch |
| 简单API调用 | ✅ Fetch |
| 大文件上传 | ✅ XHR |
| 服务端事件(SSE) | ✅ XHR |
| PWA/Service Worker | ✅ Fetch |
最终决策指南:
- 优先使用 Fetch API 满足大多数现代 Web 应用需求
- 在需要精确进度控制或兼容旧浏览器时使用 XHR
- 大型项目可封装统一 HTTP 客户端,内部智能选择最佳实现
- 关注 Fetch 规范发展,未来功能将持续增强
两者各有优势,理解其核心差异和适用场景,将帮助您为每个具体需求选择最佳解决方案。
XMLHttpRequest的response 与 Fetch的Response
XMLHttpRequest 的 response 与 Fetch 的 Response:深度对比
在 AJAX 请求中,XMLHttpRequest (XHR) 和 Fetch API 处理响应数据的方式有显著差异。以下是两者响应处理的全面对比:
一、核心差异概览
| 特性 | XMLHttpRequest | Fetch API |
|---|---|---|
| 响应对象 | XHR 实例本身 | 专用 Response 对象 |
| 数据访问 | 通过属性直接访问 | 通过解析方法获取 |
| 响应类型设置 | 请求前设置 responseType | 请求后调用解析方法 |
| HTTP 错误处理 | 需手动检查 status | 需手动检查 response.ok |
| 流式处理 | 有限支持 | 原生支持 ReadableStream |
| 数据复用 | 可多次访问响应属性 | 响应体只能读取一次 |
二、响应数据访问方式对比
1. XMLHttpRequest 的响应访问
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.responseType = 'json'; // 预先设置响应类型
xhr.onload = function() {
// 通过属性直接访问数据
if (xhr.status === 200) {
console.log('状态:', xhr.status);
console.log('响应文本:', xhr.responseText); // 文本形式
console.log('解析后数据:', xhr.response); // 根据responseType
console.log('响应头:', xhr.getResponseHeader('Content-Type'));
}
};
xhr.send();
2. Fetch 的响应访问
fetch('/api/data')
.then(response => {
console.log('状态:', response.status);
console.log('响应头:', response.headers.get('Content-Type'));
// 必须调用解析方法获取数据
return response.json(); // 返回新Promise
})
.then(data => {
console.log('解析后数据:', data);
});
三、关键差异详解
1. 响应数据类型处理
| 数据类型 | XMLHttpRequest | Fetch API |
|---|---|---|
| 文本 | xhr.responseText | response.text() |
| JSON | xhr.response (需设 responseType='json') | response.json() |
| Blob | xhr.response (需设 responseType='blob') | response.blob() |
| ArrayBuffer | xhr.response (需设 responseType='arraybuffer') | response.arrayBuffer() |
| 文档 | xhr.responseXML | 无直接等效,需 response.text() 后解析 |
| FormData | 无原生支持 | response.formData() |
2. 响应头访问对比
// XMLHttpRequest
const contentType = xhr.getResponseHeader('Content-Type');
const allHeaders = xhr.getAllResponseHeaders();
// Fetch API
const contentType = response.headers.get('Content-Type');
const headersObj = Object.fromEntries(response.headers.entries());
3. HTTP 错误处理差异
// XMLHttpRequest
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功处理
} else {
// HTTP错误处理
}
}
};
// Fetch API
fetch(url)
.then(response => {
if (!response.ok) { // 手动检查
throw new Error(`HTTP错误! 状态码: ${response.status}`);
}
return response.json();
})
.catch(error => {
// 捕获网络错误和手动抛出的错误
});
4. 数据复用能力
// XMLHttpRequest - 可多次访问
console.log(xhr.responseText); // 无限制
console.log(xhr.response);
// Fetch API - 响应体只能读取一次
fetch(url)
.then(response => {
const dataPromise = response.json(); // 首次读取
// 尝试再次读取会报错
response.text() // TypeError: Already read
.catch(e => console.error(e));
});
5. 流式数据处理
// XMLHttpRequest - 有限支持
xhr.responseType = 'arraybuffer';
xhr.onprogress = function(e) {
if (e.lengthComputable) {
const partialData = new Uint8Array(xhr.response);
}
};
// Fetch API - 原生流支持
fetch('/large-file')
.then(response => {
const reader = response.body.getReader();
function processStream({ done, value }) {
if (done) return;
console.log('收到数据块:', value);
return reader.read().then(processStream);
}
return reader.read().then(processStream);
});
四、特殊场景处理
1. 二进制数据处理
// XMLHttpRequest
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
const buffer = xhr.response;
const dataView = new DataView(buffer);
};
// Fetch API
fetch('/binary-data')
.then(response => response.arrayBuffer())
.then(buffer => {
const dataView = new DataView(buffer);
});
2. 超大型文件处理
// Fetch 流式处理更高效 (内存占用恒定)
async function processLargeFile(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let receivedLength = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 处理数据块
receivedLength += value.length;
console.log(`已接收 ${receivedLength} 字节`);
}
}
3. 服务端发送事件 (SSE)
// XMLHttpRequest 支持
const xhr = new XMLHttpRequest();
xhr.open('GET', '/sse-endpoint');
xhr.onprogress = function() {
console.log('收到部分数据:', xhr.responseText);
};
// Fetch API 不支持原生 SSE
// 需使用专用 EventSource API
const es = new EventSource('/sse-endpoint');
es.onmessage = event => {
console.log('收到事件:', event.data);
};
五、性能与内存对比
| 指标 | XMLHttpRequest | Fetch API |
|---|---|---|
| 内存占用 | 整个响应存储在内存中 | 支持流式处理,内存占用更低 |
| 大文件处理 | 可能内存溢出 | 适合处理超大文件 |
| 响应延迟 | 需等待整个响应完成 | 可流式处理首批数据 |
| GC 效率 | 整个响应对象统一回收 | 分块数据可及时回收 |
六、最佳实践指南
1. 选择建议:
-
使用 XMLHttpRequest 当:
- 需要精确的上传/下载进度
- 兼容 IE11 等旧浏览器
- 处理小到中型数据
- 需要同步请求(特殊场景)
-
使用 Fetch API 当:
- 处理大型文件或流数据
- 现代浏览器环境
- 需要更好的错误处理链
- 集成 Service Worker
2. 混合使用方案:
async function smartFetch(url, options = {}) {
// 需要进度监控时使用 XHR
if (options.trackProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', url);
// 进度处理
if (options.onProgress) {
xhr.upload.onprogress = options.onProgress;
}
xhr.onload = () => resolve({
status: xhr.status,
data: xhr.response,
headers: xhr.getAllResponseHeaders()
});
xhr.onerror = reject;
xhr.send(options.body);
});
}
// 默认使用 Fetch
try {
const response = await fetch(url, options);
const data = await response.json();
return { status: response.status, data };
} catch (error) {
throw error;
}
}
七、未来发展趋势
-
Fetch 的增强功能:
// 实验性优先级控制 fetch(url, { priority: 'high' }); // 请求诊断API const timing = performance.getEntriesByName(url)[0]; console.log('TTFB:', timing.responseStart - timing.requestStart); -
WebTransport API:
// 下一代HTTP/3传输协议 const transport = new WebTransport('https://example.com'); const stream = await transport.createBidirectionalStream(); -
响应缓存优化:
fetch(url, { cache: 'force-cache', // 强缓存 integrity: 'sha256-...' // 子资源完整性校验 });
总结:核心选择建议
-
响应数据访问:
- XHR:直接访问响应属性,更简单
- Fetch:需要调用解析方法,更现代
-
大文件处理:
- XHR:有限支持,内存占用高
- Fetch:原生流支持,内存效率高
-
错误处理:
- XHR:需手动检查状态码
- Fetch:需检查
response.ok
-
浏览器支持:
- XHR:全平台支持(包括 IE7+)
- Fetch:现代浏览器(IE 不支持)
根据具体需求选择:
- 简单请求/旧浏览器 → XMLHttpRequest
- 流处理/现代浏览器 → Fetch API
- 混合环境 → 使用抽象层封装
理解这些核心差异将帮助您为特定场景选择最合适的 HTTP 请求技术。
fetch 用例
Fetch API 实用用例大全:从基础到高级实践
Fetch API 是现代 Web 开发的基石,以下是 10 个关键场景的实用代码示例,涵盖从基础请求到高级应用:
一、基础请求场景
1. GET 请求 - 获取 JSON 数据
// 获取用户数据
async function getUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`HTTP 错误! 状态码: ${response.status}`);
}
const users = await response.json();
console.log('用户列表:', users);
return users;
} catch (error) {
console.error('获取用户失败:', error);
return [];
}
}
2. POST 请求 - 提交表单数据
// 创建新用户
async function createUser(userData) {
try {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify(userData),
credentials: 'include' // 包含 cookies
});
if (response.status === 201) {
const newUser = await response.json();
console.log('用户创建成功:', newUser);
return newUser;
} else {
const errorData = await response.json();
throw new Error(errorData.message || '创建用户失败');
}
} catch (error) {
console.error('创建用户错误:', error);
throw error;
}
}
// 使用示例
createUser({ name: 'Alice', email: 'alice@example.com' });
二、文件处理场景
3. 上传文件
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('description', '用户头像');
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
// 不需要手动设置 Content-Type,浏览器会自动设置 multipart/form-data
});
if (!response.ok) {
throw new Error('文件上传失败');
}
const result = await response.json();
console.log('文件上传成功:', result.fileUrl);
return result.fileUrl;
} catch (error) {
console.error('上传错误:', error);
throw error;
}
}
// 使用示例
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
if (e.target.files[0]) {
await uploadFile(e.target.files[0]);
}
});
4. 下载文件
async function downloadFile(fileUrl, fileName) {
try {
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error('文件下载失败');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
// 创建临时链接并触发下载
const a = document.createElement('a');
a.href = url;
a.download = fileName || 'downloaded-file';
document.body.appendChild(a);
a.click();
// 清理
setTimeout(() => {
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 100);
} catch (error) {
console.error('下载错误:', error);
alert('文件下载失败');
}
}
// 使用示例
downloadFile('https://example.com/report.pdf', '季度报告.pdf');
三、高级控制场景
5. 请求超时处理
async function fetchWithTimeout(url, options = {}, timeout = 8000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`);
}
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`请求超时 (${timeout}ms)`);
}
throw error;
}
}
// 使用示例
fetchWithTimeout('https://slow-api.com/data', {}, 5000)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('错误:', error.message));
6. 并发请求处理
async function fetchMultiple(urls) {
try {
const requests = urls.map(url =>
fetch(url).then(response => {
if (!response.ok) throw new Error(`请求失败: ${url}`);
return response.json();
})
);
const results = await Promise.allSettled(requests);
// 处理结果
const data = [];
const errors = [];
results.forEach(result => {
if (result.status === 'fulfilled') {
data.push(result.value);
} else {
errors.push(result.reason.message);
}
});
console.log('成功请求:', data.length);
console.log('失败请求:', errors);
return { data, errors };
} catch (error) {
console.error('并发请求错误:', error);
return { data: [], errors: [error.message] };
}
}
// 使用示例
fetchMultiple([
'https://api.example.com/users',
'https://api.example.com/posts',
'https://api.example.com/comments'
]);
四、特殊场景处理
7. 处理流式数据(大文件/实时数据)
async function processLargeFile(url, onChunk, onComplete) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('无法获取文件');
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let receivedLength = 0;
while (true) {
const { done, value } = await reader.read();
if (done) {
onComplete();
break;
}
receivedLength += value.length;
const chunk = decoder.decode(value, { stream: true });
// 处理数据块
onChunk(chunk, receivedLength);
}
} catch (error) {
console.error('流处理错误:', error);
}
}
// 使用示例
processLargeFile(
'https://example.com/large-log.txt',
(chunk, received) => {
console.log(`已接收 ${received} 字节`);
// 实时处理数据块
},
() => console.log('文件处理完成')
);
8. 带进度的文件上传
async function uploadWithProgress(file, onProgress) {
const controller = new AbortController();
try {
const response = await fetch('/api/upload', {
method: 'POST',
headers: {
'X-File-Name': encodeURIComponent(file.name),
'X-File-Size': file.size
},
body: file,
signal: controller.signal,
duplex: 'half' // 必需参数
});
if (!response.ok) {
throw new Error('上传失败');
}
return await response.json();
} catch (error) {
if (error.name !== 'AbortError') {
console.error('上传错误:', error);
}
throw error;
}
}
// 创建带进度监控的可读流
function trackUploadProgress(file, onProgress) {
let uploaded = 0;
const total = file.size;
const trackedFile = new ReadableStream({
start(controller) {
const reader = new FileReader();
reader.onload = function() {
controller.enqueue(new Uint8Array(reader.result));
uploaded += reader.result.byteLength;
onProgress(uploaded, total);
controller.close();
};
const chunkSize = 1024 * 1024; // 1MB 分块
let offset = 0;
function readNextChunk() {
const chunk = file.slice(offset, offset + chunkSize);
reader.readAsArrayBuffer(chunk);
offset += chunkSize;
if (offset >= file.size) {
controller.close();
}
}
readNextChunk();
}
});
return new File([trackedFile], file.name, {
type: file.type,
lastModified: file.lastModified
});
}
// 使用示例
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file) {
const trackedFile = trackUploadProgress(file, (uploaded, total) => {
const percent = Math.round((uploaded / total) * 100);
console.log(`上传进度: ${percent}%`);
});
await uploadWithProgress(trackedFile);
console.log('文件上传完成');
}
});
五、安全与优化场景
9. 带重试机制的请求
async function fetchWithRetry(url, options = {}, maxRetries = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// 服务器错误时重试
if (response.status >= 500 && attempt < maxRetries) {
throw new Error(`服务器错误: ${response.status}`);
}
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`);
}
return response;
} catch (error) {
if (attempt === maxRetries) throw error;
// 指数退避算法
const waitTime = delay * Math.pow(2, attempt - 1);
console.warn(`请求失败,${waitTime}ms后重试 (${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
}
// 使用示例
fetchWithRetry('https://unstable-api.com/data', {}, 5)
.then(response => response.json())
.then(data => console.log('最终数据:', data))
.catch(error => console.error('所有重试失败:', error));
10. 请求拦截器(全局处理)
// 保存原始 fetch 引用
const originalFetch = window.fetch;
// 自定义 fetch 实现
window.fetch = async function(url, options = {}) {
// 1. 请求前处理
console.log(`请求发起: ${url}`);
// 自动添加认证令牌
if (!options.headers) options.headers = {};
if (!options.headers['Authorization']) {
const token = localStorage.getItem('authToken');
if (token) {
options.headers['Authorization'] = `Bearer ${token}`;
}
}
// 添加请求时间戳
options.headers['X-Request-Timestamp'] = Date.now();
try {
// 2. 发起实际请求
const response = await originalFetch(url, options);
// 3. 响应后处理
console.log(`请求完成: ${url} [${response.status}]`);
// 自动刷新令牌
if (response.status === 401) {
const refreshed = await refreshToken();
if (refreshed) {
// 使用新令牌重试请求
return window.fetch(url, options);
}
}
return response;
} catch (error) {
// 4. 错误处理
console.error(`请求失败: ${url}`, error);
// 全局错误通知
showNotification(`网络错误: ${error.message}`);
throw error;
}
};
// 使用示例
// 所有 fetch 请求将自动通过此拦截器
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
// 辅助函数
async function refreshToken() {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) return false;
try {
const response = await originalFetch('/api/refresh-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ refreshToken })
});
if (response.ok) {
const { accessToken } = await response.json();
localStorage.setItem('authToken', accessToken);
return true;
}
} catch (error) {
console.error('令牌刷新失败:', error);
}
// 刷新失败,清除用户状态
localStorage.removeItem('authToken');
localStorage.removeItem('refreshToken');
return false;
}
六、实用封装函数
11. 通用请求封装
class ApiClient {
constructor(baseUrl, defaultOptions = {}) {
this.baseUrl = baseUrl;
this.defaultOptions = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
...defaultOptions
};
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
...this.defaultOptions,
...options,
headers: {
...this.defaultOptions.headers,
...(options.headers || {})
}
};
if (config.body && typeof config.body === 'object' &&
config.headers['Content-Type'] === 'application/json') {
config.body = JSON.stringify(config.body);
}
try {
const response = await fetch(url, config);
// 处理非200响应
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
} catch {
errorData = { message: `HTTP错误 ${response.status}` };
}
throw new ApiError(response.status, errorData);
}
// 处理空响应
if (response.status === 204) {
return null;
}
// 根据内容类型解析响应
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return await response.json();
} else if (contentType.includes('text/')) {
return await response.text();
} else {
return await response.blob();
}
} catch (error) {
if (error instanceof ApiError) throw error;
throw new NetworkError(error.message);
}
}
get(endpoint, options = {}) {
return this.request(endpoint, { ...options, method: 'GET' });
}
post(endpoint, body, options = {}) {
return this.request(endpoint, { ...options, method: 'POST', body });
}
put(endpoint, body, options = {}) {
return this.request(endpoint, { ...options, method: 'PUT', body });
}
delete(endpoint, options = {}) {
return this.request(endpoint, { ...options, method: 'DELETE' });
}
}
// 自定义错误类
class ApiError extends Error {
constructor(status, data) {
super(data.message || `API请求失败: ${status}`);
this.name = 'ApiError';
this.status = status;
this.data = data;
}
}
class NetworkError extends Error {
constructor(message) {
super(message || '网络错误');
this.name = 'NetworkError';
}
}
// 使用示例
const api = new ApiClient('https://api.example.com', {
credentials: 'include'
});
// 获取用户
api.get('/users/123')
.then(user => console.log('用户:', user))
.catch(error => {
if (error instanceof ApiError) {
console.error(`API错误 [${error.status}]:`, error.data);
} else {
console.error('网络错误:', error);
}
});
// 创建新项目
api.post('/projects', { title: '新项目', description: '项目描述' })
.then(project => console.log('创建的项目:', project));
关键要点总结
- 基础请求:使用
fetch().then().catch()模式处理基本请求 - 错误处理:始终检查
response.ok或response.status - 超时控制:使用
AbortController实现请求超时 - 文件处理:
- 上传:使用
FormData或二进制流 - 下载:使用
blob()和对象 URL
- 上传:使用
- 高级场景:
- 流处理:使用
ReadableStream处理大数据 - 进度监控:自定义可读流实现
- 重试机制:指数退避算法提升可靠性
- 流处理:使用
- 安全实践:
- 自动添加认证头
- 令牌刷新机制
- 全局错误处理
- 封装模式:创建可复用的 API 客户端类
这些实用代码片段可直接集成到项目中,覆盖了大多数网络请求场景的最佳实践。根据具体需求调整参数和错误处理逻辑,可以构建健壮高效的网络请求系统。