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

MSW已拦截请求但仍触发Axios网络错误问题求助

问题:MSW拦截Axios请求后仍出现网络错误?

我现在卡壳了——查了好几篇Axios+MSW的社区帖子,但还是没找到问题。我想用MSW拦截Axios发起的网络请求,注入测试响应,但测试一直失败,报网络错误,而且确认MSW已经拦截到请求了。


测试文件(执行失败,调试中)

/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { jobsDashboardResponse } from "../../../tests/mocks/msw/handlers"
import { server as mswServer } from "../../../tests/mocks/msw/server"
import { bacalhauAPI } from "../bacalhau"

// Enable request interception.
beforeAll(() => mswServer.listen())

// Reset handlers so that each test could alter them
// without affecting other, unrelated tests.
afterEach(() => mswServer.resetHandlers())

// Don't forget to clean up afterwards.
afterAll(() => mswServer.close())

describe("Basic fetch of mocked API", () => {
    it("should GET /orchestrator/jobs", async () => {
        const jobsResponse = { a: 2 }

        mswServer.use(jobsDashboardResponse)
        mswServer.listHandlers()

        const jobs = await bacalhauAPI.listJobs()

        expect(jobs).toEqual(jobsResponse)
    })
})

BacalhauAPI代码

import axios, { AxiosInstance } from "axios"
import {
  JobListRequest,
} from "../helpers/jobInterfaces"

class BacalhauAPI {
  apiClient: AxiosInstance

  constructor(baseURL: string) {
    this.apiClient = axios.create({
      baseURL,
      headers: {
        "Content-Type": "application/json",
      },
    })
  }

  async listJobs(labels?: string[], nextToken?: string): Promise<JobsResponse> {
    try {
      const params: JobListRequest = {
        order_by: "created_at",
        reverse: true,
        limit: 10,
        labels: labels ? `env in (${labels.join(",")})` : undefined,
        next_token: nextToken,
      }
      const response = await this.apiClient.get("/orchestrator/jobs", {
        params,
      })
      return response.data as JobsResponse
    } catch (error) {
      console.error("An error occurred while listing jobs:", error)
      throw error
    }
  }

}

function getAPIConfig(
  property: "host" | "port" | "base",
  defaultValue: string
): string {
  const declared = document
    .querySelector(`link[rel=api-${property}]`)
    ?.getAttribute("href")
  const useDefault =
    declared === undefined ||
    declared?.match(/\{{2}/) ||
    declared === "" ||
    declared === null
  return useDefault ? defaultValue : declared || ""
}

const host = getAPIConfig("host", document.location.hostname)
const port = getAPIConfig("port", "1234")
const base = getAPIConfig("base", "api/v1")
export const bacalhauAPI = new BacalhauAPI(
  `${document.location.protocol}//${host}:${port}/${base}`
)

MSW handlers代码

import { http, HttpResponse, RequestHandler, RequestHandlerOptions } from "msw";
import { TestData } from "../../basic/msw.tests";

export const jobsDashboardResponse = http.get('http://localhost:1234/api/v1/orchestrator/jobs', ({ cookies }) => {
  // Placeholders for messing around with cookies
  const { v } = cookies

  return HttpResponse.json(v === 'a' ? { foo: 'a' } : { bar: 'b' })
})

export const handlers: RequestHandler<any, any, any, RequestHandlerOptions>[] = [jobsDashboardResponse]

MSW server代码

import { setupServer } from "msw/node"
import { handlers } from "./handlers"

export const server = setupServer(...handlers)

错误信息

console.error
    An error occurred while listing jobs: AxiosError {
      message: 'Network Error',
      name: 'AxiosError',
      code: 'ERR_NETWORK',
      config: {
        transitional: {
          silentJSONParsing: true,
          forcedJSONParsing: true,
          clarifyTimeoutError: false
        },
        adapter: [ 'xhr', 'http' ],
        transformRequest: [ [Function: transformRequest] ],
        transformResponse: [ [Function: transformResponse] ],
        timeout: 0,
        xsrfCookieName: 'XSRF-TOKEN',
        xsrfHeaderName: 'X-XSRF-TOKEN',
        maxContentLength: -1,
        maxBodyLength: -1,
        env: { FormData: [Function], Blob: [class Blob] },
        validateStatus: [Function: validateStatus],
        headers: Object [AxiosHeaders] {
          Accept: 'application/json, text/plain, */*',
          'Content-Type': null
        },
        baseURL: 'http://localhost:1234/api/v1',
        params: {
          order_by: 'created_at',
          reverse: true,
          limit: 10,
          labels: undefined,
          next_token: undefined
        },
        method: 'get',
        url: '/orchestrator/jobs',
        data: undefined
      },
      request: XMLHttpRequest {
        open: [Function: open],
        setRequestHeader: [Function: setRequestHeader],
        send: [Function: send],
        abort: [Function: abort],
        getResponseHeader: [Function: getResponseHeader],
        getAllResponseHeaders: [Function: getAllResponseHeaders],
        overrideMimeType: [Function: overrideMimeType],
        onreadystatechange: [Getter/Setter],
        readyState: 4,
        timeout: [Getter/Setter],
        withCredentials: [Getter/Setter],
        upload: [Getter],
        responseURL: [Getter],
        status: [Getter],
        statusText: [Getter],
        responseType: [Getter/Setter],
        response: [Getter],
        responseText: [Getter],
        responseXML: [Getter],
        UNSENT: 0,
        OPENED: 1,
        HEADERS_RECEIVED: 2,
        LOADING: 3,
        DONE: 4
      }
    }

问题核心

确认MSW已拦截到请求,但仍抛出ERR_NETWORK错误,请问问题出在哪?


可能的原因及修复方案

1. URL匹配不完整(查询参数导致)

MSW默认严格匹配完整URL,包括查询参数。你的Axios请求会带上order_by、reverse等参数,但MSW handler监听的是不带参数的URL,导致匹配失败,请求落到真实网络触发错误。

修复:
使用通配符忽略查询参数,或改用相对路径匹配:

// 方案1:用通配符匹配带参数的URL
export const jobsDashboardResponse = http.get('http://localhost:1234/api/v1/orchestrator/jobs*', ({ cookies }) => {
  return HttpResponse.json(cookies.v === 'a' ? { foo: 'a' } : { bar: 'b' })
})

// 方案2:使用相对路径,依赖baseURL自动拼接
export const jobsDashboardResponse = http.get('/api/v1/orchestrator/jobs', ({ cookies }) => {
  return HttpResponse.json(cookies.v === 'a' ? { foo: 'a' } : { bar: 'b' })
})

2. 测试环境的协议错误

在Node.js测试环境(如Jest)中,document.location.protocol可能为file:而非http:,导致Axios的baseURL变成无效的file://localhost:1234/api/v1,触发网络错误。

修复:
在测试中覆盖baseURL,或修改API初始化逻辑适配非浏览器环境:

// 测试文件中添加:在启动MSW后覆盖baseURL
beforeAll(() => {
  mswServer.listen()
  bacalhauAPI.apiClient.defaults.baseURL = 'http://localhost:1234/api/v1'
})

// 或修改BacalhauAPI的协议逻辑
const protocol = typeof window === 'undefined' ? 'http:' : document.location.protocol
export const bacalhauAPI = new BacalhauAPI(
  `${protocol}//${host}:${port}/${base}`
)

3. Axios适配器冲突

MSW在Node.js环境依赖拦截http模块,但Axios默认同时启用xhr和http适配器,xhr适配器无法被MSW正确拦截,导致请求走真实网络。

修复:
强制Axios在测试环境使用http适配器:

// 测试文件顶部添加
import axios from 'axios'
import httpAdapter from 'axios/lib/adapters/http'

axios.defaults.adapter = httpAdapter

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:47:33