JS发送HTTP请求时怎么设置超时时间和自定义请求头

作者:纸飞机时间:2026-11-03 16:10:41浏览:376

详细讲解在 JavaScript 中发送 HTTP 请求时,如何使用 Fetch API、Axios 与 XMLHttpRequest 正确配置超时时间(Timeout)与自定义请求头(Headers),并剖析跨域预检与受限头部的注意事项。

在 JavaScript 中发送 HTTP 请求时,配置自定义请求头主要用于身份验证(如 Token 传递)与指明传输格式,而设置超时时间则是保障应用健壮性、防止客户端在弱网或服务端无响应时无限期挂起的核心手段。目前前端开发中主要有三种发起请求的方式:现代原生 fetch()、成熟且广泛使用的第三方库 axios,以及底层的 XMLHttpRequest。本文将分别演示这三种方式下设置超时时间与自定义请求头的具体代码实现,并梳理跨域限制与受限请求头等易错细节。

JS发送HTTP请求时怎么设置超时时间和自定义请求头
图 1:JavaScript 发送 HTTP 请求时超时时间与请求头配置全景概览

一、Fetch API:使用 AbortController 与 headers 配置项

原生的 fetch() 方法基于 Promise 设计,语法现代简洁。需要注意的是,fetch() 的配置参数中原生支持 headers 选项,但并未直接提供 timeout 选项。在 Fetch 标准中,取消请求和控制超时统一依靠 AbortController(中断控制器)实现。

1. 设置自定义请求头

配置自定义请求头非常直接,在 fetch() 的第二个参数(配置对象)中传入 headers 字段即可。支持直接使用普通对象字面量,也可以实例化 Headers 对象:

// 方式一:直接使用普通对象字面量(推荐)
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer eyJhbGciOi...',
    'X-Client-Version': '1.2.0'
  },
  body: JSON.stringify({ keyword: 'antigravity' })
});

// 方式二:使用 Headers 构造函数
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
myHeaders.append('Authorization', 'Bearer token_value');

2. 使用 AbortController 实现请求超时

Fetch 的超时控制流程如下:通过 new AbortController() 创建控制器实例,将其内部的 signal 属性传递给 fetch() 配置;随后使用 setTimeout 启动倒计时,如果在设定时间内接口未返回,则调用 controller.abort() 主动中断请求。请求一旦被中断,fetch() 返回的 Promise 将触发 reject,并抛出名称为 AbortError 的 DOMException 错误。

JS发送HTTP请求时怎么设置超时时间和自定义请求头
图 2:Fetch API 结合 AbortController 实现超时与请求头控制的时序步骤

3. 完整的 Fetch 超时与请求头封装示例

在实际业务中,建议封装一个通用的 Fetch 请求工具函数。特别提醒:一旦请求顺利完成,必须调用 clearTimeout() 清除定时器,避免即使请求成功、定时器到期后仍然触发 abort 造成内存或逻辑隐患:

/**
 * 发送带超时与自定义请求头的 Fetch 请求
 * @param {string} url 请求目标地址
 * @param {object} options 配置项(包含 headers、timeout 等)
 */
async function fetchWithTimeout(url, options = {}) {
  const { timeout = 5000, headers = {}, ...customConfig } = options;

  // 1. 实例化中断控制器
  const controller = new AbortController();
  const signal = controller.signal;

  // 2. 启动超时倒计时
  const timeoutId = setTimeout(() => {
    controller.abort(); // 超时触发中断
  }, timeout);

  try {
    const response = await fetch(url, {
      ...customConfig,
      signal, // 挂载中断信号
      headers: {
        'Content-Type': 'application/json',
        ...headers // 合并业务自定义头部
      }
    });

    // 针对 HTTP 4xx/5xx 状态码进行判断
    if (!response.ok) {
      throw new Error(`HTTP 异常: 状态码 ${response.status}`);
    }

    return await response.json();
  } catch (err) {
    if (err.name === 'AbortError') {
      throw new Error(`请求超时,超过了预设的 ${timeout}ms 未能响应`);
    }
    throw err; // 普通网络故障或业务异常
  } finally {
    // 3. 无论成功还是失败,都必须清理定时器
    clearTimeout(timeoutId);
  }
}

// 使用示例:
fetchWithTimeout('https://api.example.com/user/profile', {
  method: 'GET',
  timeout: 3000,
  headers: {
    'Authorization': 'Bearer user_access_token',
    'X-Device-Type': 'Web-Desktop'
  }
})
  .then(data => console.log('数据获取成功:', data))
  .catch(err => console.error('请求失败:', err.message));
标准演进提示:在较新的现代浏览器(Chrome 103+、Firefox 100+、Safari 16+)以及 Node.js 17.3+ 中,标准已支持静态方法 AbortSignal.timeout(ms)。如果无需向后兼容较老版本,可直接简写为 fetch(url, { signal: AbortSignal.timeout(5000), headers: { ... } }),无需手动声明 setTimeout 与 clearTimeout。

二、Axios:基于配置项与拦截器的全功能控制

Axios 是目前工程化前端项目中最常用的 HTTP 请求客户端。与原生的 Fetch 相比,Axios 原生开箱支持 timeout 配置,并且提供了完备的请求/响应拦截器,便于做全局维度的自定义请求头注入与超时错误兜底。

1. 单次请求直接配置

在单次调用 axios()、axios.get() 或 axios.post() 时,第三个参数(或第二个参数)可以直接传入 timeout 和 headers:

import axios from 'axios';

axios.post('https://api.example.com/orders', 
  { goodsId: 1001, quantity: 2 }, // 请求体数据
  {
    timeout: 6000, // 超时时间,单位毫秒(ms)
    headers: {
      'Authorization': 'Bearer token_abc_123',
      'X-Trace-Id': 'req-9876543210'
    }
  }
)
  .then(res => console.log('下单成功:', res.data))
  .catch(error => {
    // 识别超时错误
    if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
      console.error('业务提示:网络响应超时,请重试');
    } else {
      console.error('其他网络或业务异常:', error.message);
    }
  });

2. 统一实例与拦截器注入

在真实项目中,通常不会为每个接口逐一编写 Token 与超时时间,而是通过 axios.create() 创建统一实例,并在请求拦截器中集中管理请求头:

JS发送HTTP请求时怎么设置超时时间和自定义请求头
图 3:Axios 实例与拦截器在超时与请求头管理中的架构原理
// 创建统一请求实例
const apiClient = axios.create({
  baseURL: 'https://api.example.com/v1',
  timeout: 8000, // 实例全局默认超时时间为 8 秒
  headers: {
    'Content-Type': 'application/json'
  }
});

// 1. 请求拦截器:动态追加身份认证与业务头部
apiClient.interceptors.request.use(
  config => {
    const token = localStorage.getItem('auth_token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    config.headers['X-Timestamp'] = Date.now().toString();
    return config;
  },
  error => Promise.reject(error)
);

// 2. 响应拦截器:统一处理超时及异常状态
apiClient.interceptors.response.use(
  response => response.data,
  error => {
    if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
      console.error('全局警告:接口请求超时,请检查网络状况');
    }
    return Promise.reject(error);
  }
);

三、XMLHttpRequest (XHR):传统底层的事件驱动写法

在较旧的遗留系统或需要精确监听文件上传进度的场景下,传统的 XMLHttpRequest 依然有效。XHR 提供了原生的 timeout 属性与 ontimeout 事件监听,但在使用时有严格的调用顺序要求。

1. 关键执行顺序:open() 先行

在 XHR 中设置自定义请求头与超时时间,必须遵循严格的时序:

  1. 首先调用 xhr.open(method, url, async) 打开连接;
  2. 在 open() 之后、send() 之前设置 xhr.timeout = 毫秒数;
  3. 在 open() 之后、send() 之前调用 xhr.setRequestHeader(key, value) 设置自定义请求头;若在 open() 之前调用会直接报错;
  4. 最后调用 xhr.send() 发送请求。

2. 完整的 XHR 示例代码

function sendXhrRequest() {
  const xhr = new XMLHttpRequest();

  // 1. 初始化请求(必须最先执行)
  xhr.open('POST', 'https://api.example.com/upload', true);

  // 2. 设置超时时间(单位毫秒)
  xhr.timeout = 5000;

  // 3. 设置自定义请求头(必须在 open 之后,send 之前)
  xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
  xhr.setRequestHeader('Authorization', 'Bearer xhr_token_example');
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

  // 4. 监听超时事件
  xhr.ontimeout = function () {
    console.error('XHR 请求超时,触发了 ontimeout 事件回调');
  };

  // 5. 监听请求完成与响应状态
  xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('请求成功:', JSON.parse(xhr.responseText));
    } else {
      console.error('服务端返回错误状态码:', xhr.status);
    }
  };

  // 6. 监听一般网络错误(如断网)
  xhr.onerror = function () {
    console.error('发生底层网络错误');
  };

  // 7. 发送请求
  const requestBody = JSON.stringify({ reportId: 2024 });
  xhr.send(requestBody);
}

四、自定义请求头与超时的重要注意事项

在为 HTTP 请求添加请求头或调整超时时间时,开发者经常会遭遇跨域报错或浏览器拦截。理解背后的网络协议安全规则至关重要。

JS发送HTTP请求时怎么设置超时时间和自定义请求头
图 4:自定义请求头分类与其对 CORS 跨域的影响机制

1. 跨域预检请求(CORS Preflight)

根据 W3C CORS 规范,标准的“简单请求”(Simple Requests)仅允许少量请求头(如 Accept、Accept-Language、Content-Language 以及取值为特定媒体类型的 Content-Type)。

一旦你在请求头中添加了自定义字段(例如 Authorization、X-Requested-With、X-Api-Key 或将 Content-Type 设为 application/json),浏览器在发起实际请求前,会强制自动发送一个 OPTIONS 请求进行预检。

排查要点:服务端必须在预检请求的响应中配置 Access-Control-Allow-Headers 响应头,把前端发送的自定义头名称明确列出(例如 Access-Control-Allow-Headers: Authorization, Content-Type, X-Client-Version),否则浏览器控制台将报 CORS 跨域错误并直接阻断请求。

2. 浏览器受限请求头(Forbidden Header Names)

并非所有请求头都可以由前端 JS 任意指定。出于安全防御考虑,浏览器规范禁止脚本修改以下头部(由浏览器底层全权管控):

  • Cookie / Cookie2 / Set-Cookie(避免脚本篡改凭据或跨站注入)
  • Host(确保指向实际请求域名)
  • Origin / Referer(由浏览器根据当前运行上下文自动附加)
  • User-Agent(大多数现代浏览器禁止或忽略前端覆盖)
  • 所有以 Sec- 或 Proxy- 开头的头部字段

如果在代码中尝试使用 setRequestHeader('Cookie', '...') 或 headers: { 'Host': 'evil.com' },浏览器会默默忽略该修改并在开发者工具中给出 Refused to set unsafe header 的警告。

3. 合理设定超时时间与重试策略

  • 常规查询接口:建议设置在 3000ms ~ 5000ms(3到5秒),及时向用户反馈“网络较慢”比一直转圈更友好。
  • 涉及批量计算、AI生成或大文件上传:应适当放宽至 30000ms ~ 60000ms,或采用服务端轮询机制。
  • 不可在超时时盲目重试非幂等操作:如果向服务器发送的是支付扣款、新增订单等 POST 请求,客户端超时仅仅表示“未在规定时间内拿到响应”,并不代表服务端没有执行。盲目重试可能导致重复扣款或数据重复插入,必须结合后端的幂等键(Idempotency Key)设计。

五、三种方案综合对比与选型指南

为了在实际开发中准确选用合适的技术方案,下表梳理了原生 Fetch、Axios 与 XMLHttpRequest 在超时与请求头控制上的核心区别:

技术方案 自定义请求头配置 超时时间配置 超时异常类型 适用项目场景
Fetch API options.headers(对象或 Headers 实例) 无原生参数,需结合 AbortController 或 AbortSignal.timeout() AbortError(DOMException) 轻量化现代前端、无外部打包依赖的脚手架、现代 Node.js 服务端
Axios config.headers,支持全局默认头与拦截器动态注入 开箱即用 timeout: 毫秒数 error.code === 'ECONNABORTED' 中大型 Vue / React 企业级项目、需统一拦截鉴权与自动转换 JSON 场景
XMLHttpRequest xhr.setRequestHeader()(须在 open() 后执行) xhr.timeout = 毫秒数 xhr.ontimeout 事件回调 需向后兼容旧浏览器、需实时获取文件上传/下载百分比进度的特定模块
JS发送HTTP请求时怎么设置超时时间和自定义请求头
图 5:JavaScript HTTP 请求控制避坑指南与选型决策全景图

综上所述,在现代 Web 开发中,若追求零依赖和轻量级标准,推荐使用Fetch API 搭配 AbortController;若追求工程化管理与便捷拦截,Axios 的内置 timeout 与拦截器则是最稳健高效的选择。无论选用何种方案,务必牢记对自定义头部进行跨域服务端放行,并确保超时定时器得到及时清理。

作者最新文章
相关文章
更多