无需登录 数据私有 本地保存

Mock Service Worker演示 - 拦截并返回假数据

11
0
0
0
Mock Service Worker 演示
拦截器运行中
Mock 规则列表 6 条规则
发送测试请求
/api/
响应结果
等待发送请求...
请求日志 0
暂无日志,发送请求开始记录
MSW 集成代码示例 实际项目参考
// handlers.js - 定义请求处理器
import { http, HttpResponse } from 'msw'

export const handlers = [
  // 拦截 GET /api/users
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'Alice', email: 'alice@example.com' },
      { id: 2, name: 'Bob', email: 'bob@example.com' }
    ])
  }),

  // 带路径参数的处理器
  http.get('/api/users/:id', ({ params }) => {
    const { id } = params
    return HttpResponse.json({
      id: Number(id),
      name: `User ${id}`,
      email: `user${id}@example.com`
    })
  }),

  // 模拟登录
  http.post('/api/login', async ({ request }) => {
    const body = await request.json()
    if (body.username === 'admin') {
      return HttpResponse.json({ token: 'mock-jwt-token', role: 'admin' })
    }
    return HttpResponse.json(
      { error: 'Invalid credentials' },
      { status: 401 }
    )
  })
]
// browser.js - 浏览器端Service Worker设置
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'

// 创建Service Worker实例
export const worker = setupWorker(...handlers)

// 启动worker(在开发环境中)
// worker.start({
//   onUnhandledRequest: 'bypass', // 未匹配的请求放行
//   quiet: false, // 显示详细日志
// })
// main.js 或 index.js - 应用入口
import { worker } from './mocks/browser'

async function bootstrap() {
  // 开发环境中启动MSW
  if (import.meta.env.DEV) {
    await worker.start({
      onUnhandledRequest: 'bypass',
      serviceWorker: {
        url: '/mockServiceWorker.js'
      }
    })
    console.log('🚀 MSW started - mocking enabled')
  }
  
  // 启动应用
  const app = createApp()
  app.mount('#app')
}

bootstrap()
// 测试中使用MSW (Vitest/Jest)
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

const server = setupServer(...handlers)

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('fetches users successfully', async () => {
  const response = await fetch('/api/users')
  const users = await response.json()
  
  expect(response.status).toBe(200)
  expect(users).toHaveLength(2)
  expect(users[0].name).toBe('Alice')
})

test('handles login failure', async () => {
  const response = await fetch('/api/login', {
    method: 'POST',
    body: JSON.stringify({ username: 'wrong' })
  })
  expect(response.status).toBe(401)
})
常见问题与知识点

MSW 是一个 API 模拟库,它使用 Service Worker API 在网络层面拦截请求并返回模拟数据。与传统的 mock 方案(如拦截 axios/fetch 模块)不同,MSW 拦截的是真实的网络请求,因此:

与请求库无关 — 无论是 fetch、axios、XMLHttpRequest 还是 GraphQL 客户端,都能被拦截
代码零侵入 — 不需要修改任何业务代码,mock 逻辑完全独立
复用 mock 逻辑 — 同一套 handlers 可用于开发、测试、Storybook 和环境演示
真实网络体验 — 在浏览器 DevTools 的 Network 面板中可以看到被拦截的请求

特性MSWjson-serveraxios拦截器
拦截层级网络层(Service Worker)独立服务器库级别
支持请求库所有(fetch/XHR/GraphQL)所有仅axios
DevTools可见✅ 是✅ 是❌ 否
代码侵入零侵入需配置baseURL需修改拦截器
测试复用✅ 完美复用需启动服务部分复用
离线开发✅ 支持需本地服务✅ 支持

MSW 设计为仅用于开发和测试环境。在生产构建中,你应该:

1️⃣ 使用环境变量控制:if (process.env.NODE_ENV === 'development') { await worker.start() }
2️⃣ 在生产构建时完全移除 MSW 相关代码(tree-shaking)
3️⃣ Service Worker 文件 mockServiceWorker.js 不应部署到生产服务器
4️⃣ MSW 的 start() 方法在调用前会检查环境,但最佳实践是在代码层面做好隔离

安全提示:永远不要在面向用户的生产构建中启用 MSW。

MSW 2.x 支持所有基于 HTTP 的请求类型:

REST API — 完全支持 GET/POST/PUT/DELETE/PATCH 等方法
GraphQL — 提供专门的 graphql.link()graphql.query() 处理器
WebSocket — 实验性支持(通过拦截握手请求)
文件上传 — 支持 multipart/form-data 请求
Server-Sent Events — 可模拟流式响应

无论是 fetch()axiosXMLHttpRequest@apollo/client 还是 react-query,MSW 都能在底层统一拦截。

3步快速集成:

第1步:安装
npm install msw@latest --save-dev
npx msw init ./public --save

第2步:定义handlers
创建 src/mocks/handlers.js,使用 http.get/post/put/delete 定义拦截规则

第3步:启动worker
在应用入口文件中条件启动:if (dev) { await worker.start() }

详细代码请参考上方「代码示例」标签页。

本演示工具通过重写浏览器原生API来模拟MSW的拦截行为:

🔧 fetch拦截:保存原始 window.fetch,替换为自定义函数,在函数中检查mock规则并返回模拟的 Response 对象
🔧 XHR拦截:重写 XMLHttpRequest.prototypeopensend 方法

⚠️ 与真实MSW的区别:真实MSW使用Service Worker在网络线程中拦截,不修改主线程API,更加优雅且DevTools可见。本演示仅用于教学目的,展示MSW的核心概念。在生产项目中请使用官方MSW库。