模拟响应

了解如何模拟 HTTP 响应。

基础知识

¥Basics

Mock Service Worker 尊重 WHATWG Fetch API 规范,这意味着你构建的模拟响应与你进行 fetch 调用时收到的响应相同。如果你对 Response 类感到满意,你可以跳过本节之后的所有内容 - 你已经知道如何声明响应。

¥Mock Service Worker respects the WHATWG Fetch API specification, which means that the mocked responses you construct are the same responses you would receive when making a fetch call. If you are comfortable with the Response class, you can skip everything past this section—you know how to declare responses already.

要响应被拦截的请求,请从匹配的请求处理程序返回一个有效的 Response 实例:

¥To respond to an intercepted request, return a valid Response instance from the matching request handler:

import { http } from 'msw'
 
export const handlers = [
  // Intercept the "GET /resource" request.
  http.get('/resource', () => {
    // And respond with a "text/plain" response
    // with a "Hello world!" text response body.
    return new Response('Hello world!')
  }),
]

你不需要导入 Response 类,因为它是浏览器和现代版本的 Node.js (v17+) 中全局 Fetch API 的一部分。库支持 Response 实例的任何变体,包括通过 Response.json()Response.error() 等方法创建的简写响应。

¥You don’t need to import the Response class because it’s a part of the global Fetch API in the browser and modern versions of Node.js (v17+). The library supports any variations of the Response instances, including shorthand responses created via methods like Response.json() or Response.error().

使用 HttpResponse

¥Using HttpResponse class

尽管你可以使用普通的 Fetch API 响应,但强烈建议你使用库提供的自定义 HttpResponse 类。有两个原因使 HttpResponse 优于原生 Response

¥Although you can use plain Fetch API responses, it’s highly recommended you use the custom HttpResponse class provided by the library. There are two reasons to prefer HttpResponse over the native Response:

  1. HttpResponse 类封装了用于声明响应的有用简写方法,如 HttpResponse.json()HttpResponse.xml()HttpResponse.formData() 等。

    ¥The HttpResponse class encapsulates useful shorthand methods for declaring responses, like HttpResponse.json(), HttpResponse.xml(), HttpResponse.formData(), etc.

  2. 与原生 Response 类不同,HttpResponse 类通过在模拟响应上设置 Set-Cookie 标头来支持模拟响应 cookie。

    ¥Unlike the native Response class, the HttpResponse class enables support for mocking response cookies by setting the Set-Cookie header on the mocked response.

下面是相同的 GET /resource 处理程序,但使用 HttpResponse 类:

¥Here’s the same GET /resource handler but using the HttpResponse class:

// 1. Import the "HttpResponse" class from the library.
import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/resource', () => {
    // 2. Return a mocked "Response" instance from the handler.
    return HttpResponse.text('Hello world!')
  }),
]

HttpResponse 与原生 Response 类 100% 兼容。在后台,HttpResponse 返回一个简单的 Fetch API Response 实例。

¥HttpResponse is 100% compatible with the native Response class. Under the hood, HttpResponse returns a plain Fetch API Response instance.

模拟状态代码和文本

¥Mocking status code and text

在响应初始化器对象中提供 status 和/或 statusText 属性,分别指定模拟响应状态或响应状态代码。

¥Provide the status and/or statusText property in the response initializer object to specify a mocked response status or response status code, respectively.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/apples', () => {
    return new HttpResponse(null, {
      status: 404,
      statusText: 'Out Of Apples',
    })
  }),
]

模拟标头

¥Mocking headers

在响应初始化器对象中提供 headers 属性,以指定模拟响应标头。

¥Provide the headers property in the response initializer object to specify mocked response headers.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    return new HttpResponse(null, {
      headers: {
        'Set-Cookie': 'mySecret=abc-123',
        'X-Custom-Header': 'yes',
      },
    })
  }),
]

了解有关构建 标头 的更多信息。

¥Learn more about constructing Headers.

模拟主体

¥Mocking body

你可以使用各种响应主体类型来响应请求:StringBlobFormDataReadableStream 和其他(请参阅 Fetch API Response 了解支持的响应主体类型)。

¥You can respond to requests with various response body types: String, Blob, FormData, ReadableStream, and others (see Fetch API Response for supported response body types).

下面,让我们看看如何模拟一些最常见的 HTTP 响应主体。

¥Below, let’s take a look at how to mock some of the most common HTTP response bodies.

文本响应

¥Text responses

HTTP 中最基本的响应是文本响应。将你希望用来响应的文本字符串作为参数提供给 HttpResponse 构造函数,以创建模拟文本响应:

¥The most basic response in HTTP is a text response. Provide the text string you wish to respond with as an argument to the HttpResponse constructor to create a mocked text response:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/name', () => {
    return new HttpResponse('John')
  }),
]

你还可以使用 HttpResponse.text() 简写静态方法。

¥You can also use HttpResponse.text() shorthand static method.

JSON 响应

¥JSON responses

更常见的响应主体类型是 JSON。将你希望用来响应的 JSON 作为参数提供给 HttpResponse.json() 静态方法,以创建模拟 JSON 响应:

¥A much more common response body type is JSON. Provide the JSON you wish to respond with as an argument to the HttpResponse.json() static method to create a mocked JSON response:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    // Note that you DON'T have to stringify the JSON!
    return HttpResponse.json({
      user: {
        id: 'abc-123',
        name: 'John Maverick',
      },
    })
  }),
]

我们建议使用 HttpResponse.json() 简写静态方法自动使 Content-TypeContent-Length 响应标头与你正在使用的响应 JSON 主体保持同步。

¥We recommend using the HttpResponse.json() shorthand static method to automatically keep the Content-Type and Content-Length response headers in-sync with the response JSON body you’re using.

流响应

¥Stream responses

你可以使用 ReadableStream 进行响应,以将任何数据流回客户端。

¥You can respond with a ReadableStream to stream any data back to the client.

Streaming

Respond with a `ReadableStream`.

其他响应

¥Other responses

你可以使用 MSW 描述许多其他响应主体类型,例如 XML、BlobArrayBufferFormData。请参阅 HttpResponse API 以了解有关使用其他响应主体类型的更多信息。

¥There are many other response body types you can describe with MSW, like XML, Blob, ArrayBuffer, or FormData. Please refer to the HttpResponse API to learn more about using other response body types.

HttpResponse

API reference for the `HttpResponse` class.

模拟错误响应

¥Mocking error responses

从响应解析器构造并返回有效的错误响应以模拟错误响应。

¥Construct and return a valid error response from a response resolver to emulate an error response.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/user', () => {
    // Respond with "401 Unauthorized" to "GET /user" requests.
    return new HttpResponse(null, { status: 401 })
  }),
]

模拟网络错误

¥Mocking network errors

使用 Response.error()HttpResponse.error() 静态方法引发网络错误。与错误响应不同,网络错误将停止请求执行并将该请求标记为失败。实际上,在构建无效请求或请求无法解析的主机名时,你可能会遇到网络错误。

¥Use the Response.error() or HttpResponse.error() static method to raise a network error. Unlike an error response, a network error will halt the request execution and will mark that request as failed. In practice, you may experience network errors when constructing an invalid request or requesting hostnames that cannot be resolved.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/checkout/cart', () => {
    return HttpResponse.error()
  }),
]

抛出响应

¥Throwing responses

你可以在响应解析器中的任何点抛出 Response 实例。发生这种情况时,请求处理会短路,抛出的响应将作为模拟响应返回。

¥You can throw a Response instance at any point in the response resolver. When that happens, the request handling short-circuits, and the thrown response is returned as the mocked response.

import { http, HttpResponse } from 'msw'
 
http.post('/login', ({ request }) => {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
})

这对于在处理请求时实现中间件模式特别方便。

¥This is particularly handy to implement a middleware pattern when handling requests.

async function isAuthenticated(request) {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
}
 
http.post('/login', async ({ request }) => {
  await isAuthenticated(request)
 
  return new HttpResponse(null, {
    status: 302,
    headers: {
      Location: '/dashboard',
    },
  })
})

请注意,任何非响应异常都将转换为 500 Internal Server Error 响应,类似于服务器运行时发生的未处理的拒绝。

¥Note that any non-response exceptions will be translated to 500 Internal Server Error responses, similar to an unhandled rejection happening on server runtime.

相关材料

¥Related materials