pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:HTML5 Fetch API - 完整教程与代码示例
一、教程简介
Fetch API 是 HTML5 提供的现代网络请求接口,用于替代传统的 XMLHttpRequest。Fetch 基于 Promise 设计,API 更加简洁直观,支持请求和响应的流式处理,默认不发送 Cookie(需手动配置),提供了更灵活的请求控制和错误处理机制。Fetch API 是现代 Web 应用中进行 HTTP 请求的首选方案。
二、核心概念
Fetch vs XMLHttpRequest
请求配置
代码示例
fetch(url, {
method: 'GET', // 请求方法
headers: { // 请求头
'Content-Type': 'application/json'
},
body: JSON.stringify(data), // 请求体
mode: 'cors', // 模式:cors/no-cors/same-origin
credentials: 'same-origin', // Cookie:omit/same-origin/include
cache: 'default', // 缓存策略
redirect: 'follow', // 重定向:follow/error/manual
referrer: 'no-referrer', // 引用来源
signal: abortController.signal // 取消信号
});Response 对象
三、语法与用法
基本请求
代码示例
// GET 请求
const response = await fetch('/api/data');
const data = await response.json();
// POST 请求
const response = await fetch('/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '张三', age: 25 })
});错误处理
代码示例
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
const data = await response.json();
} catch (error) {
console.error('请求失败:', error);
}取消请求
代码示例
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求已取消');
}
});
// 取消
controller.abort();四、代码示例
示例一:Fetch 请求工具
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fetch API - 请求工具</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #1a1a2e;
min-height: 100vh;
padding: 30px 20px;
color: #e0e0e0;
}
.container { max-width: 700px; margin: 0 auto; }
h1 { font-size: 24px; color: #fff; margin-bottom: 8px; }
.desc { color: #7a8ba0; font-size: 14px; margin-bottom: 24px; }
.card {
background: #16213e;
border-radius: 12px;
padding: 24px;
margin-bottom: 16px;
border: 1px solid #1a3a5c;
}
.card h2 { font-size: 16px; color: #fff; margin-bottom: 16px; }
.input-group { margin-bottom: 12px; }
.input-group label {
display: block;
font-size: 12px;
color: #7a8ba0;
margin-bottom: 4px;
}
input, select, textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid #1a3a5c;
border-radius: 6px;
background: #0f3460;
color: #e0e0e0;
font-size: 13px;
outline: none;
}
input:focus, select:focus { border-color: #00cec9; }
.btn-group { display: flex; gap: 10px; flex-wrap: wrap; }
button {
padding: 8px 18px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.btn-cyan { background: #00cec9; color: #1a1a2e; }
.btn-cyan:hover { background: #00b5ad; }
.btn-red { background: #e17055; color: #fff; }
.btn-red:hover { background: #d35f48; }
.btn-orange { background: #fdcb6e; color: #1a1a2e; }
.btn-orange:hover { background: #e5b85c; }
.response-area {
background: #0a0a1a;
border-radius: 8px;
padding: 16px;
margin-top: 12px;
font-family: monospace;
font-size: 12px;
line-height: 1.7;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
.status-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
margin-bottom: 8px;
}
.status-2xx { background: #00b894; color: #fff; }
.status-4xx { background: #fdcb6e; color: #1a1a2e; }
.status-5xx { background: #e17055; color: #fff; }
.status-error { background: #636e72; color: #fff; }
.quick-urls {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.quick-url {
padding: 4px 12px;
border: 1px solid #1a3a5c;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
color: #7a8ba0;
transition: all 0.2s;
}
.quick-url:hover { border-color: #00cec9; color: #00cec9; }
.loading { color: #fdcb6e; }
.timer { font-size: 12px; color: #7a8ba0; margin-top: 8px; }
</style>
</head>
<body>
<div class="container">
<h1>Fetch 请求工具</h1>
<p class="desc">使用 Fetch API 发送 HTTP 请求,查看响应结果。</p>
<div class="card">
<h2>请求配置</h2>
<div class="quick-urls">
<span class="quick-url" onclick="setUrl('https://jsonplaceholder.typicode.com/posts/1')">获取文章</span>
<span class="quick-url" onclick="setUrl('https://jsonplaceholder.typicode.com/users')">用户列表</span>
<span class="quick-url" onclick="setUrl('https://jsonplaceholder.typicode.com/todos?_limit=5')">待办事项</span>
</div>
<div class="input-group">
<label>请求 URL</label>
<input type="text" id="reqUrl" value="https://jsonplaceholder.typicode.com/posts/1">
</div>
<div style="display:flex;gap:12px;">
<div class="input-group" style="flex:0 0 120px;">
<label>方法</label>
<select id="reqMethod">
<option>GET</option>
<option>POST</option>
<option>PUT</option>
<option>DELETE</option>
</select>
</div>
<div class="input-group" style="flex:1;">
<label>请求体(JSON)</label>
<input type="text" id="reqBody" placeholder='{"key":"value"}'>
</div>
</div>
<div class="btn-group">
<button class="btn-cyan" id="sendBtn" onclick="sendRequest()">发送请求</button>
<button class="btn-red" id="cancelBtn" onclick="cancelRequest()" disabled>取消请求</button>
</div>
</div>
<div class="card">
<h2>响应结果</h2>
<div id="responseStatus"></div>
<div class="response-area" id="responseBody">等待请求...</div>
<div class="timer" id="responseTimer"></div>
</div>
</div>
<script>
let currentController = null;
function setUrl(url) {
document.getElementById('reqUrl').value = url;
document.getElementById('reqMethod').value = 'GET';
document.getElementById('reqBody').value = '';
}
async function sendRequest() {
const url = document.getElementById('reqUrl').value.trim();
const method = document.getElementById('reqMethod').value;
const body = document.getElementById('reqBody').value.trim();
if (!url) { alert('请输入 URL'); return; }
currentController = new AbortController();
document.getElementById('sendBtn').disabled = true;
document.getElementById('cancelBtn').disabled = false;
document.getElementById('responseBody').textContent = '请求中...';
document.getElementById('responseBody').className = 'response-area loading';
document.getElementById('responseStatus').innerHTML = '';
document.getElementById('responseTimer').textContent = '';
const startTime = performance.now();
try {
const options = {
method: method,
signal: currentController.signal,
headers: {}
};
if (body && method !== 'GET') {
options.headers['Content-Type'] = 'application/json';
options.body = body;
}
const response = await fetch(url, options);
const duration = (performance.now() - startTime).toFixed(0);
const statusClass = response.ok ? 'status-2xx' :
response.status < 500 ? 'status-4xx' : 'status-5xx';
document.getElementById('responseStatus').innerHTML =
'<span class="status-badge ' + statusClass + '">' + response.status + ' ' + response.statusText + '</span>';
const contentType = response.headers.get('content-type') || '';
let result;
if (contentType.includes('json')) {
result = JSON.stringify(await response.json(), null, 2);
} else {
result = await response.text();
}
document.getElementById('responseBody').textContent = result;
document.getElementById('responseBody').className = 'response-area';
document.getElementById('responseTimer').textContent =
'耗时: ' + duration + 'ms | 大小: ' + (result.length / 1024).toFixed(1) + ' KB';
} catch (error) {
const statusClass = error.name === 'AbortError' ? 'status-error' : 'status-5xx';
document.getElementById('responseStatus').innerHTML =
'<span class="status-badge ' + statusClass + '">' + error.name + '</span>';
document.getElementById('responseBody').textContent = error.message;
document.getElementById('responseBody').className = 'response-area';
} finally {
document.getElementById('sendBtn').disabled = false;
document.getElementById('cancelBtn').disabled = true;
currentController = null;
}
}
function cancelRequest() {
if (currentController) {
currentController.abort();
}
}
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 错误处理
代码示例
// Fetch 只在网络错误时 reject,HTTP 错误不会
async function safeFetch(url, options) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error('HTTP ' + response.status + ': ' + response.statusText);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') throw error;
throw new Error('请求失败: ' + error.message);
}
}2. 超时处理
代码示例
function fetchWithTimeout(url, options = {}, timeout = 10000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, { ...options, signal: controller.signal })
.then(response => {
clearTimeout(id);
return response;
})
.catch(error => {
clearTimeout(id);
if (error.name === 'AbortError') {
throw new Error('请求超时');
}
throw error;
});
}3. Cookie 配置
代码示例
// 发送 Cookie
fetch('/api/data', { credentials: 'include' });
// 同源发送 Cookie(默认)
fetch('/api/data', { credentials: 'same-origin' });
// 不发送 Cookie
fetch('/api/data', { credentials: 'omit' });七、代码规范示例
代码示例
class HttpClient {
constructor(baseURL = '', defaultOptions = {}) {
this.baseURL = baseURL;
this.defaultOptions = defaultOptions;
this.interceptors = { request: [], response: [] };
}
async request(url, options = {}) {
const fullURL = this.baseURL + url;
let config = { ...this.defaultOptions, ...options };
// 请求拦截器
for (const interceptor of this.interceptors.request) {
config = await interceptor(config);
}
let response = await fetch(fullURL, config);
// 响应拦截器
for (const interceptor of this.interceptors.response) {
response = await interceptor(response);
}
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response;
}
get(url, options = {}) {
return this.request(url, { ...options, method: 'GET' });
}
post(url, body, options = {}) {
return this.request(url, {
...options,
method: 'POST',
headers: { 'Content-Type': 'application/json', ...options.headers },
body: JSON.stringify(body)
});
}
put(url, body, options = {}) {
return this.request(url, {
...options,
method: 'PUT',
headers: { 'Content-Type': 'application/json', ...options.headers },
body: JSON.stringify(body)
});
}
delete(url, options = {}) {
return this.request(url, { ...options, method: 'DELETE' });
}
}
// 使用
const api = new HttpClient('https://api.example.com', {
credentials: 'include'
});
const data = await api.get('/users');
const result = await api.post('/users', { name: '张三' });八、常见问题与解决方案
常见问题
Q1:Fetch 不发送 Cookie?
解决方案:设置 credentials: 'include'(跨域)或 credentials: 'same-origin'(同源)。
Q2:如何获取上传进度?
解答:Fetch API 不支持上传进度。如需进度监控,使用 XMLHttpRequest。
Q3:CORS 错误怎么处理?
解决方案:确保服务器设置了正确的 CORS 头(Access-Control-Allow-Origin 等),或使用代理服务器。
Q4:如何重试失败的请求?
解决方案:使用循环和指数退避策略实现请求重试。
代码示例
async function fetchWithRetry(url, options = {}, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
}
}
}九、总结
Fetch API 是现代 Web 开发中进行 HTTP 请求的标准方案,基于 Promise 设计,语法简洁直观。使用时需注意:HTTP 错误不会导致 Promise reject(需手动检查 response.ok)、默认不发送 Cookie(需配置 credentials)、不支持上传进度(需用 XHR)、需要配合 AbortController 实现请求取消和超时。通过封装 HTTP 客户端类可以实现拦截器、重试、超时等高级功能。
标签:
Fetch API
网络请求
Promise
HTTP
AbortController
本文涉及AI创作
内容由AI创作,请仔细甄别