前端嘗試用 XMLHttpRequest 或 fetch 獲取登入 API 的 Response Headers,但 getAllResponseHeaders() 返回空或報錯。
Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma 等「簡單回應標頭」。Authorization, X-Token) 需後端在 Access-Control-Expose-Headers 明確宣告。| 方案 | 適用場景 | 實作要點 |
|---|---|---|
| 後端配置 CORS | 控制後端 | 回應頭加 Access-Control-Allow-Origin: * (或指定域)Access-Control-Expose-Headers: Authorization, X-Custom-Header |
| 自建代理 | 無法改後端 | 前端→同源代理頁面 (Nginx/Node/Java)→目標 API,代理轉發並透傳 Header |
| YQL / 公共代理 | 僅 GET、無認證 | 將請求轉為 JSONP,不支援 POST/Header 讀取 |
| 瀏覽器擴充套件 | 開發調試 | 擴充套件擁有 webRequest 權限可攔截所有 Header |
function getHeaders(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true); // 非同步
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.getAllResponseHeaders()); // 字串,每行 "Key: Value"
} else {
reject(new Error(`HTTP ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('Network error'));
xhr.send('phone=xxx&password=yyy'); // 應用表單格式
});
}
// 使用
getHeaders('/api/login').then(headers => {
console.log(headers); // 解析字串為物件
const headerMap = headers.trim().split(/[\r\n]+/).reduce((acc, line) => {
const [k, ...v] = line.split(': ');
acc[k.toLowerCase()] = v.join(': ');
return acc;
}, {});
console.log(headerMap['authorization']);
}).catch(console.error);
fetch('/api/login', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'phone=xxx&password=yyy',
credentials: 'include' // 若需 Cookie
}).then(res => {
// res.headers 是 Headers 物件,可迭代
for (const [key, value] of res.headers.entries()) {
console.log(`${key}: ${value}`);
}
return res.json();
}).then(data => console.log(data));
xhr.open(..., false) 同步請求**已棄用**,會阻塞主執行緒,且無法在 Worker 外使用。xhr.withCredentials = true / fetch(..., {credentials: 'include'}),後端須回 Access-Control-Allow-Credentials: true 且 Access-Control-Allow-Origin 不能為 *。OPTIONS,回應 Access-Control-Allow-Methods, Access-Control-Allow-Headers。@CrossOrigin, Nginx add_header)。fetch + 封裝 request(url, options) 工具函式。