mswjs:在浏览器和 Node 环境中轻松做 API Mocking

mswjs:在浏览器和 Node 环境中轻松做 API Mocking

mswjs 是一个同时兼容浏览器和 Node 的 API mocking 库,帮助开发者在 API 还没 ready 的情况下,通过 mock 数据进行完美开发。

mswjs 原理(浏览器端)是使用 Service Worker API 拦截(网络层)实际请求实现 API 的请求、响应模拟。

官网地址:https://mswjs.io/

下面我们会创建一个 Next.js 项目,来演示 mwsjs 的使用。

创建项目 & 安装依赖

npx create-next-app mws-app 

√ Would you like to use TypeScript with this project? ... No / Yes
√ Would you like to use ESLint with this project? ... No / Yes
√ Would you like to use Tailwind CSS with this project? ... No / Yes
√ Would you like to use `src/` directory with this project? ... No / Yes
√ Use App Router (recommended)? ... No / Yes
√ Would you like to customize the default import alias? ... No / Yes
Creating a new Next.js app in D:learningmws-app.

cd mws-app 
pnpm install mws --save-dev

pnpm run dev
> next dev

- ready started server on 0.0.0.0:3000, url: http://localhost:3000

定义 mocks

按照约定,我们将 mock 定义放在 src/mocks 目录中:

mkdir mocks
# 创建 handlers.js 文件
touch src/mocks/handlers.js

Mocking API

mwsjs 支持两种类型的 API Mock,一个是 REST API,一个是 GraphQL API。如果你知道两者的区别,或者压根就不知道 GraphQL,也没有关系,无脑看成  REST API 就行,因为我们 99.9% 的项目都是用 REST API。

我们编辑 handlers.js

import { rest } from 'msw'

rest 就是一个服务器(类似 express()),用于处理请求和响应,我们下面分别介绍。

Request handler

假设我们要做一个基础的登录功能。需要准备两个接口

  • POST /login:登录使用
  • GET /user:现在登录用户信息

先创建请求处理器 Request handler,使用 rest[METHOD] 方式:

// src/mocks/handlers.js
import { rest } from 'msw'

export const handlers = [
  // Handles a POST /login request
  rest.post('https://example.com/login'null),

  // Handles a GET /user request
  rest.get('/user'null),
]

handlers.js 文件是通过一个叫 handlers 的命名导出暴露拦截的/要 Mock 的请求的。

Response resolver

前一步已经做到请求拦截了,这一步我们在 Mock 响应数据:

import { rest } from 'msw'

export const handlers = [
  rest.post('https://example.com/login', (req, res, ctx) => {
    // Persist user's authentication in the session
    sessionStorage.setItem('is-authenticated''true')

    return res(
      // Respond with a 200 status code
      ctx.status(200),
    )
  }),

  rest.get('/user', (req, res, ctx) => {
    // Check if the user is authenticated in this session
    const isAuthenticated = sessionStorage.getItem('is-authenticated')

    if (!isAuthenticated) {
      // If not authenticated, respond with a 403 error
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage'Not authorized',
        }),
      )
    }

    // If authenticated, return a mocked user details
    return res(
      ctx.status(200),
      ctx.json({
        username'admin',
      }),
    )
  }),
]

响应回调函数接受 3 个参数:

  • req:匹配请求的信息
  • res:用于创建模拟响应的功能函数
  • ctx:包含一系列用于设置模拟响应的状态码、头部、正文等功能的函数

整合

当然,上面这些约定 Mocking API 写好之后,并不会自动生效,我们还需要整合到现有项目中。

当然,这块我们根据浏览器端、Node 端分别介绍。

浏览器端

准备

Mock Service Worker  在客户端是通过 Service Worker 做请求拦截的,因此这块我们要单独启动个服务器服务 来自 mwsjs Service Worker 文件中的代码请求 。

这块代码不需要我们手写,直接使用 npx msw init <PUBLIC_DIR> --save 指令就可以了。这里的 <PUBLIC_DIR> 就是指项目的静态资源的存放目录,对 Next.js 来说,就是项目跟路径下的 public/ 目录:

npx msw init public/ --save

执行完成后,会在 public/ 下,看到多出一个 mockServiceWorker.js 文件。**mockServiceWorker.js**** 不需要我们做任何修改,放在这里就行了。**这个 worker 脚本会被注入到浏览器网页中,用于拦截请求。

注意,这里的 --save 是必须的,这个选项会在 package.json 文件中写一个 msw 字段。

{
  // ...
  "msw": {
    "workerDirectory""public"
  }
}

这样日后更新 msw 包的时候,public/ 目录下的 worker 脚本会自动更新。

配置 worker

接下来在创建 src/mocks/browser.js 文件:

import { setupWorker } from 'msw'
import { handlers } from './handlers'

// This configures a Service Worker with the given request handlers.
export const worker = setupWorker(...handlers)

这一步将我们定义的 handlers 绑定到 mws 上(这时候定义的 Mock API 才与 mws 有了联系)。

我们导出 worker 实例。

启动 worker

worker 实例准备好后,就要在合适的时机启动 worker 了(worker.start()):

对 Next.js 项目而言,我们在 pages/_app.tsx 中引入:

import '@/styles/globals.css'
import type { AppProps } from 'next/app'

async function initMocks() {
if (typeof window !== 'undefined') {
const { worker } = await import('../mocks/browser')
worker.start()
}
}

initMocks()

export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}

我们只在开发环境下才进行 API Mock。现在,localhost:3000 打开网页,看见下面的打印结果就说明接入成功了。

mswjs:在浏览器和 Node 环境中轻松做 API Mocking

然后添加按钮,模拟登录:

export default function Home() {  
function handleLogin() {
fetch('https://example.com/login', { method: 'post' }).then(res => res.statusText).then(console.log)
}

function handleGetUser() {
fetch('/user').then(res => res.json()).then(console.log)
}

return (
<p>
<button onClick={handleLogin}>Login</button>
<button onClick={handleGetUser}>Get User</button>
</p>
)
}

发现接口 Mock 成功了!

mswjs:在浏览器和 Node 环境中轻松做 API Mocking

Node 端

配置服务器

创建 src/mocks/server.js 文件:

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

// This configures a request mocking server with the given request handlers.
export const server = setupServer(...handlers)

setupServer() 使用之前定义好的 handlers 创建一个服务器。

需要注意的是,非 DOM 环境下的请求 URL 都必须使用绝对地址(absolute request URLs)。

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

export const server = setupServer(
  // NOT "/user", nothing to be relative to!
  rest.get('https://api.backend.dev/user', (req, res, ctx) => {
    return res(ctx.json({ firstName'John' }))
  }),
  ...handlers
)

在我们创建的 Next.js 项目中,默认会有一个 src/pages/api/hello.ts 的 API 路由。我们可以利用它来作为我们测试后端接口请求的方式:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import https from 'node:https'
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
  name: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
{
  const request = https.request('https://api.backend.dev/user', (response) => {
    let data = ''

    response.on('data', (chunk) => {
      data = data + chunk.toString()
    });

    response.on('end', () => {
      const body = JSON.parse(data)
      res.status(200).json(body)
    });
  })

  request.end()
}

或者借助 axios:

pnpm install axios
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import axios from 'axios'

type Data = {
  name: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
{
  axios.get('https://api.backend.dev/user')
    .then(response => res.status(200).json(response.data))
}

注意,不要使用 Fetch API!mwsjs 是通过拦截 Node 的 http/https module 实现 API Mocking 的,因此 Node 中原生支持的 fetch API 会 Mock 失败的。

启动服务器

pages/_app.tsx 中引入:

import '@/styles/globals.css'
import type { AppProps } from 'next/app'

async function initMocks({
  if (typeof window !== 'undefined') {
    const { worker } = await import('../mocks/browser')
    worker.start()
  } else {
    const { server } = await import('../mocks/server')
    server.listen()
  }
}

initMocks()

export default function App({ Component, pageProps }: AppProps{
  return <Component {...pageProps} />
}

浏览器访问 http://localhost:3000/api/hello 就能看到正确的 Mock 数据了。

mswjs:在浏览器和 Node 环境中轻松做 API Mocking

区分 dev/prod 环境

目前我们的代码在开发(development,对应 next dev)和生产环境(production,对应 next start)都会启用 API Mocking,这不是我们想要的。

因此,我们需要控制「只在开发环境进行 API Mocking」,我们可以借助 Next.js 的环境文件[1]支持。

首先,在项目根目录下创建两个文件:.env.development.env.production

NEXT_PUBLIC_API_MOCKING=enabled
NEXT_PUBLIC_API_MOCKING=disabled

Next.js 会将 NEXT_PUBLIC_ 做前缀的环境变量,在浏览器和 Node 环境中都设置。如上所示,开发环境我们启用 mwsjs,生产环境则禁用 mwsjs。

接下来调整一下 pages/_app.tsx 中的代码,判断仅在 API Mocking 启用的情况下,引入 mwsjs 配置。

import '@/styles/globals.css'
import type { AppProps } from 'next/app'

async function initMocks({
  if (typeof window !== 'undefined') {
    const { worker } = await import('../mocks/browser')
    worker.start()
  } else {
    const { server } = await import('../mocks/server')
    server.listen()
  }
}

if (process.env.NEXT_PUBLIC_API_MOCKING === 'enabled') {
  initMocks()
}

export default function App({ Component, pageProps }: AppProps{
  return <Component {...pageProps} />
}

当然,如果感觉 _app.tsx 中的代码很多,也可以将 initMocks 函数放到 mocks/index.js 文件中:

async function initMocks({
  if (typeof window !== 'undefined') {
    const { worker } = await import('./browser')
    worker.start()
  } else {
    const { server } = await import('./server')
    server.listen()
  }
}

initMocks()

然后修改 pages/_app.tsx 文件:

import '@/styles/globals.css'
import type { AppProps } from 'next/app'

if (process.env.NEXT_PUBLIC_API_MOCKING === 'enabled') {
  require('../mocks')
}

export default function App({ Component, pageProps }: AppProps{
  return <Component {...pageProps} />
}

还是一样的效果。

好了,关于 mwsjs 的介绍就到这里啦,感谢各位的阅读!

最后

书写的过程中,参考了一些资料:

  • https://mswjs.io/docs/getting-started/install
  • https://www.geeksforgeeks.org/node-js-https-request-function/
  • https://Github.com/vercel/next.js/blob/canary/examples/with-msw
  • https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables

参考资料

[1]

Next.js 的环境文件: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables

原文始发于微信公众号(写代码的宝哥):mswjs:在浏览器和 Node 环境中轻松做 API Mocking

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/243949.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!