You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在客户端脚本中检测Azure DevOps REST API的401错误?

如何在客户端脚本中检测Azure DevOps REST API的401未授权错误

问题描述

使用过期/无效的PAT调用Azure DevOps REST API时,浏览器DevTools控制台会显示:

GET https://dev.azure.com/contoso/_apis/connectionData net::ERR_FAILED 401 (Unauthorized)

但在JavaScript中使用fetch(包括then/catch、await+try/catch)或XMLHttpRequest时,仅能捕获到通用错误:

TypeError: Failed to fetch

同时浏览器会抛出CORS相关限制提示:

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.

即使Network标签页明确显示401状态,设置mode: '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安全策略的限制:当请求返回401未授权时,Azure DevOps服务器未返回Access-Control-Allow-Origin响应头,浏览器会直接拦截完整响应,不让JavaScript代码获取具体状态码,仅抛出通用的TypeError。

可行解决方案

1. 使用后端代理(推荐)

客户端不直接调用Azure DevOps API,通过自有后端服务转发请求:

  • 后端调用Azure DevOps API不受浏览器CORS限制,可正常获取401状态码
  • 后端将状态码和响应内容返回给客户端,客户端即可精准判断PAT是否无效

示例后端逻辑(Node.js):

const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.get('/api/ado/:endpoint', async (req, res) => {
  const pat = req.headers['x-pat'];
  const endpoint = req.params.endpoint;
  const url = `https://dev.azure.com/contoso/_apis/${endpoint}`;

  try {
    const response = await fetch(url, {
      headers: {
        'Authorization': `Basic ${Buffer.from(`:${pat}`).toString('base64')}`,
        'Accept': 'application/json'
      }
    });
    
    res.status(response.status);
    res.json(await response.json());
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(3000, () => console.log('Proxy server running on port 3000'));

客户端修改为调用代理接口:

async function ado(api) {
  const url = `http://localhost:3000/api/ado/${api}`;
  const response = await fetch(url, {
    method: 'GET',
    headers: {
      'X-PAT': pat,
      'Accept': 'application/json',
    }
  });
  
  if (!response.ok) {
    if (response.status === 401) {
      throw new Error('无效或过期的PAT');
    }
    throw new Error(`请求失败: ${response.status}`);
  }
  
  return await response.json();
}

2. 配置Azure DevOps CORS白名单(需管理员权限)

若你拥有Azure DevOps组织管理员权限,可将客户端域名添加到组织级CORS白名单:

  • 登录Azure DevOps组织,进入组织设置 -> 安全性 -> CORS
  • 添加客户端域名(如http://localhost:8080)
  • 保存后,Azure DevOps会在响应中返回Access-Control-Allow-Origin头,客户端即可直接获取401状态码

注意:此方法不适用于本地文件(origin: null)场景,必须部署到有明确域名的服务器上。

3. 纯客户端环境的妥协方案

若无后端代理权限,也无法修改Azure DevOps CORS设置,纯客户端脚本无法精准捕获401状态。此时可通过捕获TypeError: Failed to fetch,结合用户输入PAT的场景,提示用户检查PAT是否有效或过期,但无法100%确认是401导致的错误。

内容的提问来源于stack exchange,提问作者zumalifeguard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:06:03