pin_drop当前位置:知识文库 ❯ 图文
后端通信:HTTP头部详解 - 从入门到实践详解
教程简介
HTTP头部(Header)是HTTP报文中不可或缺的组成部分,它承载了请求和响应的元数据信息。从内容协商(Content-Type、Accept)到缓存控制(Cache-Control、ETag),从认证授权(Authorization、Cookie)到跨域通信(CORS相关头部),HTTP头部几乎控制着Web通信的方方面面。深入理解HTTP头部的工作机制,是掌握前后端通信、优化应用性能、保障安全的关键。
本教程将全面讲解HTTP请求头、响应头、内容类型头部、认证头部、缓存头部、CORS头部、Cookie头部以及自定义头部的使用方法和最佳实践。
核心概念
1. HTTP头部概述
HTTP头部是键值对形式出现的元数据,位于请求行/状态行之后、请求体/响应体之前,以空行分隔。
格式规范:
每行一个头部字段:
Name: Value头部名称不区分大小写
冒号后可以有空格也可以没有
空行表示头部结束
2. 头部分类
3. 通用头部详解
Content-Type
指定请求体或响应体的媒体类型(MIME类型):
Accept
客户端声明能够处理的响应内容类型:
代码示例
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8q表示权重(quality value),范围0-1,默认1*/*表示接受任何类型
Authorization
携带认证凭据的请求头:
代码示例
// Basic认证
Authorization: Basic dXNlcjpwYXNzd29yZA==
// Bearer Token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// API Key
Authorization: ApiKey abc123def456Cache-Control
控制缓存行为的头部,是最重要的缓存控制机制:
请求中的Cache-Control:
响应中的Cache-Control:
Cookie相关头部
请求头 - Cookie:浏览器自动携带Cookie
代码示例
Cookie: sessionId=abc123; theme=dark; lang=zh-CN响应头 - Set-Cookie:服务器设置Cookie
代码示例
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600Cookie属性说明:
CORS相关头部
请求头:
代码示例
Origin: https://www.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization响应头:
代码示例
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Expose-Headers: X-Custom-Header4. 其他重要头部
请求头
响应头
5. 自定义头部
自定义头部以X-前缀开头(传统惯例,RFC 6648已废弃此惯例):
代码示例
// 请求自定义头部
X-Api-Version: 2
X-Request-ID: abc123
X-Client-Version: 1.0.0
// 响应自定义头部
X-Request-ID: abc123
X-RateLimit-Limit: 100
X-Total-Count: 500注意:自定义头部可能触发CORS预检请求。
语法与用法
Fetch API中设置和读取头部
代码示例
// 设置请求头
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'X-Custom-Header': 'custom-value'
},
body: JSON.stringify({ name: '张三' })
});
// 读取响应头
const contentType = response.headers.get('Content-Type');
const requestId = response.headers.get('X-Request-Id');
const rateLimit = response.headers.get('X-RateLimit-Remaining');
// 遍历响应头
response.headers.forEach((value, name) => {
console.log(`${name}: ${value}`);
});
// 使用Headers对象
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Accept', 'text/html');
const hasAuth = headers.has('Authorization');
headers.delete('X-Temp-Header');代码示例
示例1:HTTP头部浏览器与编辑器
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTTP头部浏览器与编辑器</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: #f0f2f5;
padding: 20px;
color: #333;
}
.container { max-width: 1000px; margin: 0 auto; }
h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
.card {
background: #fff;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.card h2 {
font-size: 18px;
color: #1a73e8;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid #e8eaed;
}
.header-category {
margin-bottom: 20px;
}
.category-title {
font-size: 15px;
font-weight: 700;
padding: 8px 14px;
border-radius: 6px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.cat-content { background: #e8f5e9; color: #2e7d32; }
.cat-auth { background: #fce4ec; color: #c62828; }
.cat-cache { background: #fff3e0; color: #ef6c00; }
.cat-cors { background: #e3f2fd; color: #1565c0; }
.cat-cookie { background: #f3e5f5; color: #7b1fa2; }
.cat-custom { background: #e0f2f1; color: #00695c; }
.header-item {
display: flex;
align-items: flex-start;
padding: 12px;
border-bottom: 1px solid #f0f0f0;
gap: 12px;
}
.header-item:last-child { border-bottom: none; }
.header-name {
font-family: 'Consolas', monospace;
font-weight: 700;
font-size: 14px;
min-width: 200px;
color: #1a73e8;
}
.header-value {
font-family: 'Consolas', monospace;
font-size: 13px;
color: #555;
flex: 1;
}
.header-desc {
font-size: 12px;
color: #999;
margin-top: 4px;
}
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
}
.tag-request { background: #e3f2fd; color: #1565c0; }
.tag-response { background: #e8f5e9; color: #2e7d32; }
.tag-both { background: #fff3e0; color: #ef6c00; }
.filter-bar {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.filter-btn {
padding: 6px 14px;
border: 1px solid #dadce0;
border-radius: 20px;
background: #fff;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.filter-btn.active { background: #1a73e8; color: #fff; border-color: #1a73e8; }
.search-input {
width: 100%;
padding: 10px 16px;
border: 1px solid #dadce0;
border-radius: 8px;
font-size: 14px;
margin-bottom: 16px;
}
.search-input:focus { outline: none; border-color: #1a73e8; }
</style>
</head>
<body>
<div class="container">
<h1>HTTP头部浏览器与编辑器</h1>
<div class="card">
<h2>搜索与筛选</h2>
<input type="text" class="search-input" id="searchInput" placeholder="搜索头部名称或描述...">
<div class="filter-bar">
<div class="filter-btn active" onclick="filterBy('all')">全部</div>
<div class="filter-btn" onclick="filterBy('request')">请求头</div>
<div class="filter-btn" onclick="filterBy('response')">响应头</div>
<div class="filter-btn" onclick="filterBy('content')">内容类型</div>
<div class="filter-btn" onclick="filterBy('auth')">认证</div>
<div class="filter-btn" onclick="filterBy('cache')">缓存</div>
<div class="filter-btn" onclick="filterBy('cors')">CORS</div>
</div>
</div>
<div id="headerList"></div>
</div>
<script>
const headers = [
{ name: 'Content-Type', type: 'both', category: 'content', value: 'application/json', desc: '指定请求体或响应体的MIME类型。API常用application/json,表单用application/x-www-form-urlencoded,文件上传用multipart/form-data。' },
{ name: 'Content-Length', type: 'both', category: 'content', value: '1024', desc: '请求体或响应体的大小(字节)。浏览器和服务器通常自动设置。' },
{ name: 'Content-Encoding', type: 'response', category: 'content', value: 'gzip', desc: '响应体使用的编码方式,如gzip、deflate、br(Brotli)。用于压缩传输减少带宽。' },
{ name: 'Content-Disposition', type: 'response', category: 'content', value: 'attachment; filename="file.pdf"', desc: '指示响应内容如何显示。attachment表示下载,inline表示内联显示。' },
{ name: 'Accept', type: 'request', category: 'content', value: 'application/json, text/html;q=0.9', desc: '客户端声明能够处理的响应内容类型。q值表示优先级权重(0-1)。' },
{ name: 'Accept-Encoding', type: 'request', category: 'content', value: 'gzip, deflate, br', desc: '客户端支持的压缩编码方式。服务器据此选择合适的压缩算法。' },
{ name: 'Accept-Language', type: 'request', category: 'content', value: 'zh-CN,zh;q=0.9,en;q=0.8', desc: '客户端偏好的自然语言。服务器据此返回对应语言的内容。' },
{ name: 'Authorization', type: 'request', category: 'auth', value: 'Bearer eyJhbGci...', desc: '携带认证凭据。常用格式:Basic(Base64编码的用户名密码)、Bearer(JWT令牌)。' },
{ name: 'WWW-Authenticate', type: 'response', category: 'auth', value: 'Bearer realm="api"', desc: '401响应中指示客户端应使用的认证方式。' },
{ name: 'Cookie', type: 'request', category: 'cookie', value: 'sessionId=abc123; theme=dark', desc: '浏览器自动携带的Cookie值。包含该域名下所有未过期的Cookie。' },
{ name: 'Set-Cookie', type: 'response', category: 'cookie', value: 'sessionId=abc123; HttpOnly; Secure; SameSite=Strict', desc: '服务器设置Cookie。可指定Path、Domain、HttpOnly、Secure、SameSite等属性。' },
{ name: 'Cache-Control', type: 'both', category: 'cache', value: 'max-age=3600, public', desc: '最重要的缓存控制头部。请求中可设no-cache/no-store,响应中可设max-age/public/private等。' },
{ name: 'ETag', type: 'response', category: 'cache', value: '"abc123def456"', desc: '资源的唯一标识符。客户端下次请求时通过If-None-Match携带,服务器据此判断资源是否变化。' },
{ name: 'If-None-Match', type: 'request', category: 'cache', value: '"abc123def456"', desc: '条件请求头部。携带上次响应的ETag值,服务器比对后返回304或新资源。' },
{ name: 'Last-Modified', type: 'response', category: 'cache', value: 'Mon, 22 Apr 2026 00:00:00 GMT', desc: '资源最后修改时间。配合If-Modified-Since使用。' },
{ name: 'If-Modified-Since', type: 'request', category: 'cache', value: 'Mon, 22 Apr 2026 00:00:00 GMT', desc: '条件请求头部。携带上次响应的Last-Modified值。' },
{ name: 'Expires', type: 'response', category: 'cache', value: 'Mon, 22 Apr 2026 12:00:00 GMT', desc: '缓存过期时间。被Cache-Control: max-age优先级覆盖。HTTP/1.0遗留头部。' },
{ name: 'Access-Control-Allow-Origin', type: 'response', category: 'cors', value: 'https://www.example.com', desc: 'CORS核心头部。指定允许访问资源的源。*表示允许任何源,但不允许携带凭据。' },
{ name: 'Access-Control-Allow-Methods', type: 'response', category: 'cors', value: 'GET, POST, PUT, DELETE', desc: '预检响应中声明允许的HTTP方法。' },
{ name: 'Access-Control-Allow-Headers', type: 'response', category: 'cors', value: 'Content-Type, Authorization', desc: '预检响应中声明允许的请求头部。' },
{ name: 'Access-Control-Allow-Credentials', type: 'response', category: 'cors', value: 'true', desc: '指示浏览器是否暴露响应给前端JavaScript。设为true时Allow-Origin不能为*。' },
{ name: 'Access-Control-Max-Age', type: 'response', category: 'cors', value: '86400', desc: '预检请求结果的缓存时间(秒)。减少OPTIONS请求次数。' },
{ name: 'Origin', type: 'request', category: 'cors', value: 'https://www.example.com', desc: '标识请求来源的源(协议+域名+端口)。CORS和同源策略依赖此头部。' },
{ name: 'Host', type: 'request', category: 'content', value: 'api.example.com:8080', desc: '请求的目标主机名和端口。HTTP/1.1唯一必需的请求头部,支持虚拟主机。' },
{ name: 'User-Agent', type: 'request', category: 'content', value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...', desc: '客户端标识字符串。包含浏览器名称、版本、操作系统等信息。' },
{ name: 'Referer', type: 'request', category: 'content', value: 'https://example.com/page', desc: '请求来源页面的URL。用于统计分析和防盗链。注意拼写是Referer而非Referrer。' },
{ name: 'Location', type: 'response', category: 'content', value: 'https://new.example.com/page', desc: '重定向目标URL。配合3xx状态码使用,201响应中指向新创建的资源。' },
{ name: 'X-Request-ID', type: 'both', category: 'custom', value: 'req-abc123def456', desc: '请求追踪ID。用于分布式系统中追踪请求链路,便于问题排查。' },
{ name: 'X-RateLimit-Limit', type: 'response', category: 'custom', value: '100', desc: 'API限流上限。告知客户端当前时间窗口内允许的最大请求数。' },
{ name: 'X-RateLimit-Remaining', type: 'response', category: 'custom', value: '95', desc: '剩余请求次数。客户端可据此调整请求频率。' },
];
let currentFilter = 'all';
function renderHeaders(search = '') {
const container = document.getElementById('headerList');
const lowerSearch = search.toLowerCase();
const filtered = headers.filter(h => {
const matchFilter = currentFilter === 'all' ||
h.type === currentFilter ||
h.category === currentFilter;
const matchSearch = !search ||
h.name.toLowerCase().includes(lowerSearch) ||
h.desc.includes(search);
return matchFilter && matchSearch;
});
const categories = {};
filtered.forEach(h => {
if (!categories[h.category]) categories[h.category] = [];
categories[h.category].push(h);
});
const catNames = {
content: { name: '内容类型头部', css: 'cat-content' },
auth: { name: '认证授权头部', css: 'cat-auth' },
cache: { name: '缓存控制头部', css: 'cat-cache' },
cors: { name: 'CORS跨域头部', css: 'cat-cors' },
cookie: { name: 'Cookie相关头部', css: 'cat-cookie' },
custom: { name: '自定义头部', css: 'cat-custom' }
};
container.innerHTML = '';
for (const [cat, items] of Object.entries(categories)) {
const catInfo = catNames[cat] || { name: cat, css: '' };
container.innerHTML += `
<div class="card">
<div class="category-title ${catInfo.css}">${catInfo.name}</div>
${items.map(h => `
<div class="header-item">
<div>
<div class="header-name">${h.name}</div>
<span class="tag tag-${h.type === 'both' ? 'both' : h.type}">${h.type === 'both' ? '请求/响应' : h.type === 'request' ? '请求头' : '响应头'}</span>
</div>
<div style="flex:1;">
<div class="header-value">${h.value}</div>
<div class="header-desc">${h.desc}</div>
</div>
</div>
`).join('')}
</div>
`;
}
}
function filterBy(type) {
currentFilter = type;
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
event.currentTarget.classList.add('active');
renderHeaders(document.getElementById('searchInput').value);
}
document.getElementById('searchInput').addEventListener('input', (e) => {
renderHeaders(e.target.value);
});
renderHeaders();
</script>
</body>
</html>示例2:请求头配置与响应头查看器
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>请求头配置与响应头查看器</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: #f0f2f5;
padding: 20px;
color: #333;
}
.container { max-width: 900px; margin: 0 auto; }
h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
.card {
background: #fff;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.card h2 {
font-size: 18px;
color: #1a73e8;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid #e8eaed;
}
.header-row {
display: flex;
gap: 8px;
margin-bottom: 8px;
align-items: center;
}
.header-row input, .header-row select {
padding: 8px 12px;
border: 1px solid #dadce0;
border-radius: 6px;
font-size: 14px;
}
.header-row .name-input { width: 200px; font-family: 'Consolas', monospace; }
.header-row .value-input { flex: 1; font-family: 'Consolas', monospace; }
.remove-btn {
padding: 6px 12px;
background: #ea4335;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
}
.add-btn {
padding: 8px 16px;
background: #34a853;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
margin-top: 8px;
}
.preset-btns {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.preset-btn {
padding: 6px 14px;
border: 1px solid #dadce0;
border-radius: 6px;
background: #fff;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.preset-btn:hover { border-color: #1a73e8; color: #1a73e8; }
.send-btn {
padding: 12px 32px;
background: #1a73e8;
color: #fff;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
}
.send-btn:hover { background: #1557b0; }
.url-input {
width: 100%;
padding: 12px 16px;
border: 1px solid #dadce0;
border-radius: 8px;
font-size: 15px;
margin-bottom: 16px;
}
.url-input:focus { outline: none; border-color: #1a73e8; }
.response-headers {
background: #1e1e1e;
color: #d4d4d4;
padding: 16px;
border-radius: 8px;
font-family: 'Consolas', monospace;
font-size: 13px;
line-height: 1.8;
white-space: pre-wrap;
}
.header-highlight { color: #569cd6; }
.value-highlight { color: #ce9178; }
.info-note {
padding: 12px;
background: #e3f2fd;
border-radius: 6px;
font-size: 13px;
color: #1565c0;
margin-top: 12px;
}
</style>
</head>
<body>
<div class="container">
<h1>请求头配置与响应头查看器</h1>
<div class="card">
<h2>请求配置</h2>
<input type="text" class="url-input" id="urlInput" value="https://httpbin.org/get" placeholder="输入请求URL">
<h3 style="font-size:15px;margin-bottom:10px;color:#555;">预设头部模板</h3>
<div class="preset-btns">
<div class="preset-btn" onclick="applyPreset('json')">JSON API</div>
<div class="preset-btn" onclick="applyPreset('form')">表单提交</div>
<div class="preset-btn" onclick="applyPreset('auth')">带认证</div>
<div class="preset-btn" onclick="applyPreset('cors')">跨域请求</div>
<div class="preset-btn" onclick="applyPreset('cache')">缓存控制</div>
</div>
<h3 style="font-size:15px;margin-bottom:10px;color:#555;">自定义请求头</h3>
<div id="headersContainer">
<div class="header-row">
<input type="text" class="name-input" value="Accept" placeholder="头部名称">
<input type="text" class="value-input" value="application/json" placeholder="头部值">
<button class="remove-btn" onclick="removeRow(this)">删除</button>
</div>
</div>
<button class="add-btn" onclick="addRow()">+ 添加头部</button>
<div style="margin-top:20px;text-align:center;">
<button class="send-btn" onclick="sendRequest()">发送请求</button>
</div>
</div>
<div class="card" id="responseCard" style="display:none;">
<h2>响应头</h2>
<div class="response-headers" id="responseHeaders"></div>
<div class="info-note" id="corsNote" style="display:none;">
注意:由于浏览器CORS限制,部分响应头可能无法通过JavaScript读取。
服务器需要设置 Access-Control-Expose-Headers 来暴露自定义头部。
</div>
</div>
</div>
<script>
function addRow(name = '', value = '') {
const container = document.getElementById('headersContainer');
const row = document.createElement('div');
row.className = 'header-row';
row.innerHTML = `
<input type="text" class="name-input" value="${name}" placeholder="头部名称">
<input type="text" class="value-input" value="${value}" placeholder="头部值">
<button class="remove-btn" onclick="removeRow(this)">删除</button>
`;
container.appendChild(row);
}
function removeRow(btn) {
const rows = document.querySelectorAll('.header-row');
if (rows.length > 1) btn.parentElement.remove();
}
function applyPreset(type) {
const container = document.getElementById('headersContainer');
container.innerHTML = '';
const presets = {
json: [
['Accept', 'application/json'],
['Content-Type', 'application/json']
],
form: [
['Content-Type', 'application/x-www-form-urlencoded']
],
auth: [
['Authorization', 'Bearer YOUR_TOKEN_HERE'],
['Accept', 'application/json']
],
cors: [
['Origin', window.location.origin],
['Access-Control-Request-Method', 'GET']
],
cache: [
['Cache-Control', 'no-cache'],
['If-None-Match', '"etag_value"']
]
};
(presets[type] || []).forEach(([name, value]) => addRow(name, value));
}
async function sendRequest() {
const url = document.getElementById('urlInput').value;
const rows = document.querySelectorAll('.header-row');
const headers = {};
rows.forEach(row => {
const name = row.querySelector('.name-input').value.trim();
const value = row.querySelector('.value-input').value.trim();
if (name && value) headers[name] = value;
});
try {
const response = await fetch(url, {
method: 'GET',
headers: headers
});
let headerText = `<span class="header-highlight">HTTP/${response.status < 200 ? '1.1' : '1.1'}</span> <span style="color:#4caf50">${response.status} ${response.statusText}</span>\n\n`;
response.headers.forEach((value, name) => {
headerText += `<span class="header-highlight">${name}:</span> <span class="value-highlight">${value}</span>\n`;
});
document.getElementById('responseHeaders').innerHTML = headerText;
document.getElementById('responseCard').style.display = 'block';
document.getElementById('corsNote').style.display = 'block';
} catch (error) {
document.getElementById('responseHeaders').textContent = `请求失败: ${error.message}\n\n可能原因:\n1. 目标服务器不支持CORS\n2. 网络连接问题\n3. URL格式错误`;
document.getElementById('responseCard').style.display = 'block';
document.getElementById('corsNote').style.display = 'block';
}
}
</script>
</body>
</html>示例3:Content-Type与请求体格式演示
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Content-Type与请求体格式演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: #f0f2f5;
padding: 20px;
color: #333;
}
.container { max-width: 900px; margin: 0 auto; }
h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
.card {
background: #fff;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.card h2 {
font-size: 18px;
color: #1a73e8;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid #e8eaed;
}
.type-tabs {
display: flex;
gap: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.type-tab {
padding: 10px 18px;
border: 2px solid #e8eaed;
border-radius: 8px;
background: #fff;
cursor: pointer;
font-size: 13px;
font-weight: 600;
transition: all 0.2s;
}
.type-tab.active { background: #1a73e8; color: #fff; border-color: #1a73e8; }
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 16px;
border-radius: 8px;
font-family: 'Consolas', monospace;
font-size: 13px;
line-height: 1.7;
white-space: pre-wrap;
word-break: break-all;
}
.block-label {
font-size: 13px;
font-weight: 600;
color: #555;
margin-bottom: 8px;
}
.form-demo {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
margin-top: 16px;
}
.form-demo label {
display: block;
font-weight: 600;
margin-bottom: 4px;
font-size: 14px;
}
.form-demo input {
padding: 8px 12px;
border: 1px solid #dadce0;
border-radius: 6px;
font-size: 14px;
width: 100%;
margin-bottom: 10px;
}
.form-demo button {
padding: 8px 20px;
background: #1a73e8;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.result-area {
margin-top: 12px;
padding: 12px;
background: #1e1e1e;
color: #d4d4d4;
border-radius: 6px;
font-family: 'Consolas', monospace;
font-size: 12px;
white-space: pre-wrap;
word-break: break-all;
}
</style>
</head>
<body>
<div class="container">
<h1>Content-Type与请求体格式</h1>
<div class="card">
<h2>选择Content-Type</h2>
<div class="type-tabs">
<div class="type-tab active" onclick="showType('json')">application/json</div>
<div class="type-tab" onclick="showType('urlencoded')">x-www-form-urlencoded</div>
<div class="type-tab" onclick="showType('multipart')">multipart/form-data</div>
<div class="type-tab" onclick="showType('plain')">text/plain</div>
</div>
<div id="typeContent"></div>
</div>
<div class="card">
<h2>实时演示</h2>
<div class="form-demo">
<label>姓名</label>
<input type="text" id="demoName" value="张三">
<label>邮箱</label>
<input type="email" id="demoEmail" value="zhangsan@example.com">
<label>年龄</label>
<input type="number" id="demoAge" value="25">
<button onclick="generatePayload()">生成请求体</button>
</div>
<div class="result-area" id="payloadResult">点击按钮生成不同Content-Type的请求体</div>
</div>
</div>
<script>
let currentType = 'json';
const typeInfo = {
json: {
desc: '最常用的API请求格式。数据以JSON字符串形式发送在请求体中。',
headers: 'Content-Type: application/json',
body: '{"name":"张三","email":"zhangsan@example.com","age":25}',
jsCode: `fetch('/api/users', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n name: '张三',\n email: 'zhangsan@example.com',\n age: 25\n })\n});`,
pros: ['结构化数据,支持嵌套', '前后端通用格式', '可读性好', 'API标准格式'],
cons: ['不支持文件上传', '需要JSON.stringify转换']
},
urlencoded: {
desc: 'HTML表单默认编码格式。数据以key=value形式编码,用&连接。',
headers: 'Content-Type: application/x-www-form-urlencoded',
body: 'name=%E5%BC%A0%E4%B8%89&email=zhangsan%40example.com&age=25',
jsCode: `fetch('/api/users', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded'\n },\n body: new URLSearchParams({\n name: '张三',\n email: 'zhangsan@example.com',\n age: 25\n }).toString()\n});`,
pros: ['HTML表单默认格式', '简单键值对', '兼容性好'],
cons: ['不支持文件上传', '不支持嵌套结构', '中文需要URL编码']
},
multipart: {
desc: '多部分表单格式,用于文件上传。每个字段用boundary分隔符分开。',
headers: 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW',
body: '------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="name"\n\n张三\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="email"\n\nzhangsan@example.com\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="avatar"; filename="photo.jpg"\nContent-Type: image/jpeg\n\n(二进制文件数据)\n------WebKitFormBoundary7MA4YWxkTrZu0gW--',
jsCode: `const formData = new FormData();\nformData.append('name', '张三');\nformData.append('email', 'zhangsan@example.com');\nformData.append('avatar', fileInput.files[0]);\n\nfetch('/api/users', {\n method: 'POST',\n body: formData // 注意:不要手动设置Content-Type\n});`,
pros: ['支持文件上传', '可混合文本和文件', '浏览器自动处理boundary'],
cons: ['请求体较大', '不要手动设置Content-Type', '解析稍复杂']
},
plain: {
desc: '纯文本格式,数据以原始文本形式发送。',
headers: 'Content-Type: text/plain',
body: '张三\nzhangsan@example.com\n25',
jsCode: `fetch('/api/data', {\n method: 'POST',\n headers: {\n 'Content-Type': 'text/plain'\n },\n body: '张三\\nzhangsan@example.com\\n25'\n});`,
pros: ['最简单的格式', '无需编码转换'],
cons: ['无结构化', '不适合复杂数据', '服务器解析困难']
}
};
function showType(type) {
currentType = type;
document.querySelectorAll('.type-tab').forEach(t => t.classList.remove('active'));
event.currentTarget.classList.add('active');
const info = typeInfo[type];
document.getElementById('typeContent').innerHTML = `
<p style="line-height:1.8;color:#555;margin-bottom:16px;">${info.desc}</p>
<div class="comparison">
<div>
<div class="block-label">请求头</div>
<div class="code-block">${info.headers}</div>
</div>
<div>
<div class="block-label">请求体</div>
<div class="code-block">${info.body}</div>
</div>
</div>
<div style="margin-top:16px;">
<div class="block-label">JavaScript代码</div>
<div class="code-block">${info.jsCode}</div>
</div>
<div style="margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div>
<div class="block-label" style="color:#2e7d32;">优点</div>
<ul style="font-size:14px;line-height:2;color:#555;">${info.pros.map(p => `<li>${p}</li>`).join('')}</ul>
</div>
<div>
<div class="block-label" style="color:#c62828;">缺点</div>
<ul style="font-size:14px;line-height:2;color:#555;">${info.cons.map(c => `<li>${c}</li>`).join('')}</ul>
</div>
</div>
`;
}
function generatePayload() {
const name = document.getElementById('demoName').value;
const email = document.getElementById('demoEmail').value;
const age = document.getElementById('demoAge').value;
let result = '';
switch (currentType) {
case 'json':
result = `Content-Type: application/json\n\n${JSON.stringify({ name, email, age: parseInt(age) }, null, 2)}`;
break;
case 'urlencoded':
result = `Content-Type: application/x-www-form-urlencoded\n\n${new URLSearchParams({ name, email, age }).toString()}`;
break;
case 'multipart':
const fd = new FormData();
fd.append('name', name);
fd.append('email', email);
fd.append('age', age);
let multipart = `Content-Type: multipart/form-data; boundary=----Boundary\n\n`;
for (const [key, value] of fd.entries()) {
multipart += `------Boundary\nContent-Disposition: form-data; name="${key}"\n\n${value}\n`;
}
multipart += `------Boundary--`;
result = multipart;
break;
case 'plain':
result = `Content-Type: text/plain\n\n${name}\n${email}\n${age}`;
break;
}
document.getElementById('payloadResult').textContent = result;
}
showType('json');
</script>
</body>
</html>浏览器兼容性
注意事项与最佳实践
1. Content-Type选择
API请求优先使用
application/json文件上传使用
multipart/form-data,不要手动设置Content-Type普通表单使用
application/x-www-form-urlencoded使用FormData时浏览器自动设置Content-Type和boundary
2. 安全头部设置
Cookie始终设置HttpOnly和Secure属性
使用SameSite=Strict或Lax防止CSRF攻击
敏感Token放在Authorization头部而非URL参数
避免在自定义头部中传递敏感信息
3. CORS头部注意
Access-Control-Allow-Origin设为*时不能携带Cookie
自定义头部会触发CORS预检请求
使用Access-Control-Expose-Headers暴露自定义响应头
合理设置Access-Control-Max-Age减少预检请求
4. 缓存头部使用
优先使用Cache-Control而非Expires
静态资源使用Cache-Control: max-age=31536000, immutable配合内容哈希
API响应使用Cache-Control: no-cache或max-age=短时间
使用ETag实现精确的缓存验证
代码规范示例
统一的请求头部管理
代码示例
class HeaderManager {
constructor() {
this.defaultHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
}
// 获取带认证的头部
getAuthHeaders() {
const token = this.getStoredToken();
return {
...this.defaultHeaders,
'Authorization': `Bearer ${token}`,
'X-Request-ID': crypto.randomUUID()
};
}
// 获取文件上传头部(不设置Content-Type)
getUploadHeaders() {
const token = this.getStoredToken();
return {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
};
}
// 获取带缓存控制的头部
getCacheHeaders(etag) {
return {
...this.getAuthHeaders(),
'If-None-Match': etag,
'Cache-Control': 'no-cache'
};
}
getStoredToken() {
return localStorage.getItem('access_token') || '';
}
}常见问题与解决方案
问题1:自定义头部导致CORS预检
问题描述:添加Authorization或自定义X-头部后,浏览器先发送OPTIONS预检请求。
解决方案:服务器正确配置CORS头部,设置合理的Access-Control-Max-Age缓存预检结果。
问题2:FormData手动设置Content-Type
问题描述:使用FormData上传文件时手动设置Content-Type,导致boundary丢失。
解决方案:使用FormData时不要设置Content-Type,浏览器会自动设置包含boundary的正确值。
问题3:无法读取自定义响应头
问题描述:fetch API无法读取X-Request-ID等自定义响应头。
解决方案:服务器设置Access-Control-Expose-Headers: X-Request-ID, X-Total-Count暴露自定义头部。
问题4:Cookie SameSite限制
问题描述:跨站请求时Cookie不被发送。
解决方案:根据场景选择SameSite值:Strict(最严格)、Lax(默认,适中的安全级别)、None(需要配合Secure)。
总结
HTTP头部是控制Web通信行为的核心机制,本教程涵盖了以下关键内容:
内容类型头部:Content-Type决定数据格式,Accept进行内容协商
认证头部:Authorization携带凭据,Basic/Bearer/API Key等不同认证方案
缓存头部:Cache-Control是最重要的缓存控制机制,ETag/Last-Modified用于缓存验证
CORS头部:Access-Control-*系列头部控制跨域访问权限
Cookie头部:Cookie/Set-Cookie管理状态,HttpOnly/Secure/SameSite保障安全
自定义头部:X-前缀头部用于扩展功能,注意CORS和兼容性问题
正确使用HTTP头部可以优化性能(缓存)、保障安全(认证、CORS)、改善用户体验(内容协商),是每个前端开发者必须掌握的核心技能。
常见问题
什么是HTTP头部详解?
HTTP头部详解是HTML5开发中的重要技术,本教程详细介绍了其核心概念和实践方法。
如何学习HTTP头部详解的实际应用?
教程中提供了完整的代码示例和实践指导,建议结合示例代码动手练习。
HTTP头部详解有哪些注意事项?
常见注意事项包括兼容性、性能优化等,教程的注意事项与最佳实践部分有详细说明。
HTTP头部详解适合初学者吗?
本教程从基础概念讲起,循序渐进,适合有一定HTML和JavaScript基础的初学者。
HTTP头部详解的核心要点是什么?
核心要点包括教程简介等内容,建议按教程顺序逐步学习。
本文由小确幸生活整理发布,转载请注明出处
本文涉及AI创作
内容由AI创作,请仔细甄别