IT评测·应用市场-qidao123.com
标题:
解释 HTTP 中的内容协商,怎样根据客户端偏好返回符合的内容?
[打印本页]
作者:
钜形不锈钢水箱
时间:
2025-3-9 20:59
标题:
解释 HTTP 中的内容协商,怎样根据客户端偏好返回符合的内容?
一、什么是内容协商?
内容协商(Content Negotiation)是HTTP协议中客户端和服务端协商返回内容格式的机制。
就像顾客进餐厅点餐时说"我要牛排,五分熟不要香菜",服务端根据请求头中的偏好设置返回最符合的内容版本。
典型协商维度:
语言(Accept-Language)
内容类型(Accept)
编码(Accept-Encoding)
字符集(Accept-Charset)
二、协商机制工作原理
客户端请求时携带:
GET /data HTTP/1.1
Accept: application/json;q=0.9, text/html;q=0.8
Accept-Language: en-US, en;q=0.9, zh-CN;q=0.7
Accept-Encoding: gzip, deflate
复制代码
服务端响应:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: en-US
Content-Encoding: gzip
Vary: Accept, Accept-Language, Accept-Encoding
复制代码
三、服务端处理实战示例
// Express示例 - 处理多语言内容
const express = require('express');
const accepts = require('accepts'); // 重量级库推荐
const app = express();
// 中间件解析语言偏好
app.get('/news', (req, res) => {
const accept = accepts(req);
// 按优先级获取最佳语言
const langs = ['en', 'zh', 'ja'];
const language = accept.language(langs) || 'en';
// 根据语言返回内容
const content = {
en: { title: "Breaking News" },
zh: { title: "突发新闻" },
ja: { title: "速報" }
};
res.json(content[language]);
});
// 处理内容类型协商
app.get('/data', (req, res) => {
const accept = req.headers.accept || '';
if (accept.includes('application/json')) {
res.type('json').send({ data: "JSON format" });
} else if (accept.includes('text/html')) {
res.type('html').send('<div>HTML format</div>');
} else {
// 默认回退方案
res.type('text').send('Default format');
}
});
复制代码
四、前端开辟使用建议
正确设置请求头
// Axios示例:主动声明内容偏好
axios.get('/api/data', {
headers: {
'Accept': 'application/json, text/plain;q=0.8',
'Accept-Language': 'zh-CN, en;q=0.7'
}
});
复制代码
处理多语言资源
<!-- 配合lang属性实现精准匹配 -->
<html lang="zh-CN">
<script type="application/json" id="i18n">
{
"en": {"greeting": "Hello"},
"zh-CN": {"greeting": "你好"},
"zh-TW": {"greeting": "你好"}
}
</script>
</html>
复制代码
缓存控制计谋
// 必须声明Vary头避免错误缓存
app.use((req, res, next) => {
res.set('Vary', 'Accept-Language, Accept');
next();
});
复制代码
五、注意事项及常见题目
权重排序陷阱
错误实现:
// 错误:未正确处理q值权重
const langs = req.headers['accept-language'].split(',');
return langs[0]; // 直接取第一个
复制代码
正确方式应解析q参数:
// 正确解析示例
function parseLanguage(header) {
return header.split(',')
.map(lang => {
const [value, q = '1'] = lang.split(';q=');
return { value: value.trim(), q: parseFloat(q) };
})
.sort((a, b) => b.q - a.q);
}
复制代码
缓存雪崩题目
错误设置:
# 错误:忽略Vary头配置
location /data {
proxy_cache_key "$uri";
}
复制代码
正确设置:
# 正确:包含协商头作为缓存key
location /data {
proxy_cache_key "$uri$http_accept$http_accept_language";
proxy_cache_valid 200 1h;
}
复制代码
移动端特殊处理
// 识别移动设备返回优化内容
function mobileOptimized(req, res, next) {
const ua = req.headers['user-agent'];
const isMobile = /Mobile|iP(hone|od)|Android/.test(ua);
req.accepts = req.accepts || [];
if(isMobile) {
req.accepts.unshift('text/html; version=mobile');
}
next();
}
复制代码
六、进阶技巧
主动协商覆盖
// 允许URL参数覆盖Header设置
app.use((req, res, next) => {
if(req.query.format) {
req.headers.accept = `${req.query.format}, ${req.headers.accept}`;
}
next();
});
复制代码
多维度综合匹配
// 综合设备类型+语言+内容类型决策
function decideResponse(req) {
const device = detectDevice(req);
const lang = detectLanguage(req);
const format = detectFormat(req);
return {
view: `${device}/${lang}/view.${format}`,
headers: {
'Content-Type': getMimeType(format),
'Content-Language': lang
}
};
}
复制代码
七、调试技巧
cURL测试下令
# 测试多语言支持
curl -H "Accept-Language: zh-CN;q=0.8, en;q=0.9" http://api.example.com/news
# 测试内容类型协商
curl -H "Accept: text/html, application/json;q=0.9" http://api.example.com/data
复制代码
Chrome开辟者工具
Network面板右键表头 -> 点击"Accept"等头名称 -> 可快速编辑重发请求
八、总结建议
优先使用标准化的内容协商机制,而非自行发明轮子
对于复杂场景建议使用成熟库:
Node.js: accepts, negotiator
Python: werkzeug.http.parse_accept_header
Java: ContentNegotiationStrategy
始终设置Vary响应头避免缓存污染
在API文档中明白声明支持的内容类型
对不支持的类型返回406 Not Acceptable而非强行响应
正确实施内容协商可以使你的Web应用:
提升多语言支持质量
优化差别装备的用户体验
进步API的兼容性
减少不须要的网络传输
避免客户端解析错误
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4