当我使用过期的 PAT 尝试访问 REST 端点时,在浏览器的开发工具控制台窗口中我将得到:
GET https://dev.azure.com/contoso/_apis/connectionData net::ERR_FAILED 401 (Unauthorized)
但是从 JavaScript 内部不可能得到 401 状态码这一事实。 我尝试过使用“then”/“catch”/获取并使用try/catch等待。 我也尝试过 XMLHttpRequest。 我得到的唯一错误是:
TypeError: Failed to fetch
由于浏览器清楚地看到
401 (Unauthorized)
,我想检测到该状态代码。
浏览器开发工具控制台还具有以下功能:
Access to fetch at 'https://dev.azure.com/contoso/_apis/connectionData' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
“网络”选项卡还显示 401 状态:
使用
no-cors
隐藏会使所有内容变得不透明,并且您不会获得 401 状态。
这是一个可运行的示例:
<!DOCTYPE html>
<html>
<body>
<script>
(async function () {
"use strict";
const API_ORGANIZATION="contoso"
const expiredPat = 'ts44tplifj4hbbzeitcz5dg5az4q3mpqcxqmrxo4zwlgy3scobzq'
const pat = expiredPat
// Fetch data from Azure DevOps
async function ado(api) {
const url = `https://dev.azure.com/${API_ORGANIZATION}/_apis/${api}`
console.log(`url: ${url}`)
const response = await fetch(url, {
method: 'GET',
cache: 'no-cache',
mode: 'cors',
headers: {
Authorization: 'Basic ' + btoa(':' + pat),
Accept: 'application/json',
}
})
return await response.json()
}
// get the connectionData from Azure DevOps
async function getConnectionData() {
return await ado('connectionData')
}
function topText(text) {
var p = document.createElement('p');
p.innerHTML = text
document.body.prepend(p)
return p
}
// show the connection Data at the top of the window
async function showConnectionData() {
try {
const result = await getConnectionData();
topText(`Azure DevOps access authenticated as: ${result.authenticatedUser.providerDisplayName}`)
} catch(err) {
const p = topText(`${err} - See console`)
p.style.color = "red";
p.style.fontWeight = "999"
}
}
async function tryFetch() {
try {
await showConnectionData()
} catch(err) {
console.log(err);
}
}
document.addEventListener("DOMContentLoaded", function(event) {
tryFetch()
});
})();
</script>
</body>
</html>
该问题是由 CORS(跨源资源共享)引起的。
根据这个官方文档关于Azure DevOps REST API:
Azure DevOps Services 支持 CORS,这使得从 dev.azure.com/* 以外的域提供的 JavaScript 代码能够向 Azure DevOps Services REST API 发出 Ajax 请求。每个请求都必须提供凭据(PAT 和 OAuth 访问令牌都是受支持的选项)。
这意味着当您提供正确的凭据时,Azure DevOps Services 支持 CORS。当 PAT 不正确时,服务器会响应 401 未经授权状态,并且由于凭据无效,服务器不会继续处理请求,也不包含 CORS 标头。
例如,我使用以下脚本测试了该问题(将 orgname 和 pat 替换为实际值):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azure DevOps CORS Example</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<h1>Azure DevOps API Response</h1>
<div id="status"></div>
<div id="response"></div>
<script>
$(document).ready(function() {
$.ajax({
url: 'https://dev.azure.com/{orgname}/_apis/connectionData',
dataType: 'json',
headers: {
'Authorization': 'Basic ' + btoa("" + ":" + "pat")
}
}).done(function(results) {
$('#status').text('Status: 200 OK');
$('#response').text(JSON.stringify(results, null, 2));
}).fail(function(jqXHR, textStatus, errorThrown) {
$('#status').text('Status: ' + jqXHR.status + ' ' + textStatus);
$('#response').text('Response: ' + errorThrown);
});
});
</script>
</body>
</html>
使用正确的 PAT 运行脚本时,您可以在响应标头中看到
Access-Control-Allow-Origin:*
。
使用过期的 PAT 运行脚本时,响应标头中没有
Access-Control-Allow-Origin:*
。
要解决 CORS 问题,您可以参考此问题了解更多详细信息。
在我的测试中,我尝试使用 CORS Anywhere,这是 CORS Anywhere 服务的公开演示。它允许您在向不同域发出请求时绕过 CORS 限制。这只能用于开发目的。
步骤:
https://cors-anywhere.herokuapp.com
添加到脚本中的 URL,即变为 https://cors-anywhere.herokuapp.com/https://dev.azure.com/orgname/_apis/connectionData
Access-Control-Allow-Origin:*
并获取401状态码。