拦截请求

了解如何拦截传出的请求。

为了发送请求的模拟响应,我们必须先拦截该请求。在 Mock Service Worker 中,请求拦截由称为请求处理程序的函数执行。

¥In order to send a mocked response for a request, we have to intercept that request first. In Mock Service Worker, request interception is performed by functions called request handlers.

请求处理程序函数如下所示:

¥A request handler function looks like this:

// Intercept an HTTP GET request which path
// matches the "predicate", and resolve it
// using the given "resolver" function.
http.get(predicate, resolver)
 
// A similar request handler for a GraphQL mutation.
graphql.mutation(predicate, resolver)

了解有关 请求处理程序 的更多信息。

¥Learn more about Request handlers.

在本页的上下文中,我们将重点介绍请求处理程序如何使我们能够拦截请求,暂时忽略 resolver 部分。根据我们模拟的 API 类型,请求拦截的方式会有所不同,反映该 API 的域语言。

¥In the context of this page, we will focus on how request handlers enable us to intercept request, omitting the resolver part for now. The way the request interception is done will differ based on the API type we are mocking, reflecting the domain language of that API.

HTTP 请求

¥HTTP requests

可以使用 http 请求命名空间拦截 HTTP 请求。该命名空间中的方法代表 HTTP 方法(http.get()http.post() 等),并具有相同的函数调用签名,需要两个参数:

¥HTTP requests can be intercepted using the http request namespace. Methods in that namespace represent HTTP methods (http.get(), http.post(), etc.) and have the same function call signature expecting two arguments:

  • predicatestring | RegExp),请求路径谓词;

    ¥predicate (string | RegExp), a request path predicate;

  • resolver,(Response resolver),决定如何处理拦截请求的函数。

    ¥resolver, (Response resolver), a function that decides how to handle an intercepted request.

例如,这是一个返回宠物列表的 GET /pets 请求的请求处理程序:

¥For example, here’s a request handler for a GET /pets request that returns a list of pets:

import { http, HttpResponse } from 'msw'
 
http.get(
  // The "/pets" string is a path predicate.
  // Only the GET requests whose path matches
  // the "/pets" string will be intercepted.
  '/pets',
  // The function below is a "resolver" function.
  // It accepts a bunch of information about the
  // intercepted request, and decides how to handle it.
  ({ request, params, cookies }) => {
    return HttpResponse.json(['Tom', 'Jerry', 'Spike'])
  }
)

了解有关 http 请求命名空间的更多信息。

¥Learn more about the http request namespace.

HTTP 请求匹配

¥HTTP request matching

predicate 参数的性质允许你通过多个标准拦截 HTTP 请求。让我们仔细看看编写请求谓词的所有可能方法。

¥The nature of the predicate argument allows you to intercept HTTP requests by multiple criteria. Let’s take a closer look at all possible ways to write a request predicate.

请求路径名

¥Request pathname

predicate 参数是字符串时,只有路径名与该字符串匹配的请求才会被拦截。这对于相对和绝对 URL 都同样有效!

¥When the predicate argument is a string, only the requests whose pathname matches that string will be intercepted. This works the same for both relative and absolute URLs!

export const handlers = [
  http.get('/pets', petsResolver),
  http.post('https://api.github.com/repo', repoResolver),
]

你可以在请求谓词字符串中包含特殊标记以同时拦截多个请求。最常见的标记之一是通配符 (*),它可以匹配其位置上的任何字符串:

¥You can include special tokens in the request predicate string to intercept multiple requests at the same time. One of the most common tokens is a wildcard (*), which matches any string in its place:

// Intercept all GET requests to "/user":
// - GET /user
// - GET /user/abc-123
// - GET /user/abc-123/settings
http.get('/user/*', userResolver)

MSW 中的请求路径名匹配使用 path-to-regexp 库。在其文档中了解有关所有受支持的路径令牌的更多信息。

¥Request pathname matching in MSW is using path-to-regexp library. Learn more about all the supported path tokens in its documentation.

提供请求路径名时,请确保排除任何查询参数。查询参数对资源标识没有影响,而是作为向服务器发送附加信息的手段。如果 MSW 遇到查询参数,它会将其删除并打印一条警告,提示你也应该将其删除。查询参数的值仍可在响应解析器中的 request.url 属性中找到。

¥When providing request pathnames, make sure to exclude any query parameters. Query parameters have no effect on resource identification and instead serve as the means to send additional information to the server. If MSW encounters a query parameter, it removes it and prints a warning that you should remove it too. The values of the query parameters is still available to you in the request.url property in the response resolver.

正则表达式

¥Regular expression

你可以使用正则表达式作为请求路径谓词。只有 URL 与提供的表达式匹配的请求才会被拦截。利用正则表达式的动态特性来处理更高级的请求匹配场景。

¥You can use Regular expressions as the request path predicate. Only the request whose URL matches the provided expression will be intercepted. Utilize the dynamic nature of regular expressions to handle more advanced request matching scenarios.

// Intercept DELETE requests matching the regular expression.
// - DELETE /settings/sessions
// - DELETE /settings/messages
http.delete(/\/settings\/(sessions|messages)/, resolver)

GraphQL API 请求

¥GraphQL API requests

GraphQL 服务器通常通过 HTTP 实现,因此你可以使用相同的 http 请求命名空间来拦截和解析它们。但是,MSW 使用指定的 graphql 请求命名空间为拦截 GraphQL API 提供了一流的支持。该命名空间的方法代表 GraphQL 操作类型(graphql.query()graphql.mutation()),并具有相同的函数调用签名,需要以下参数:

¥GraphQL servers are often implemented via HTTP so you can use the same http request namespace to intercept and resolve them. However, MSW provides a first-class support for intercepting GraphQL APIs using a designated graphql request namespace. Methods of that namespace represents GraphQL operation types (graphql.query(), graphql.mutation()) and have the same function call signature expecting the following arguments:

例如,这是一个返回宠物列表的 ListPets 查询的请求处理程序:

¥For example, here’s a request handler for a ListPets query that returns a list of pets:

import { graphql, HttpResponse } from 'msw'
 
export const handlers = [
  graphql.query('ListPets', () => {
    return HttpResponse.json({
      data: {
        pets: [
          { id: 1, name: 'Tom' },
          { id: 2, name: 'Jerry' },
          { id: 3, name: 'Spike' },
        ],
      },
    })
  }),
]

GraphQL 请求匹配

¥GraphQL request matching

默认情况下,MSW 根据操作类型和操作名称匹配所有 GraphQL 请求,无论其端点如何。可视化它的最简单方法是想象你的应用进行的实际 GraphQL 查询:

¥By default, MSW matches all GraphQL requests, regardless of their endpoint, by their operation type and operation name. The easiest way to visualize it is to imagine an actual GraphQL query your application makes:

query ListPets {
  pets {
    id
    name
  }
}

在上面的查询中,query 是操作类型,而 ListPets 是操作名称。

¥In the query above, query is the operation type while ListPets is the operation name.

让我们看一下定义 GraphQL 请求谓词的所有可能方法。

¥Let’s take a look at all possible ways to define a GraphQL request predicate.

GraphQL 操作名称

¥GraphQL operation name

当 GraphQL 请求谓词是字符串时,只有名称与该字符串匹配的操作才会被拦截。

¥When the GraphQL request predicate is a string, only the operations whose name matches that string will be intercepted.

// Intercept GraphQL queries that match the provided name.
// - "query GetUser { ... }"
graphql.query('GetUser', userResolver)

正则表达式

¥Regular expression

与 HTTP 请求匹配类似,你可以使用正则表达式来更好地控制应该拦截哪些 GraphQL 操作名称。

¥Similar to the HTTP request matching, you can use Regular expressions to gain more control over what GraphQL operation names should be intercepted.

// Intercept GraphQL mutations that match the regular expression.
// - "mutation CreateUser { ... }"
// - "mutation CreateAuthor { ... }"
graphql.mutation(/(CreateUser|CreateAuthor)/, resolver)

端点 URL

¥Endpoint URL

在更高级的情况下,你可以通过将其绑定到特定的 HTTP 端点来缩小 GraphQL 请求拦截范围。当你需要区分针对应用中的不同 GraphQL API 执行的相同操作类型/名称时,这特别有用。

¥In more advanced scenarios, you can narrow down the GraphQL request interception by binding it to a specific HTTP endpoint. This is particularly useful when you need to distinguish between same operation types/names that are performed against different GraphQL APIs in your application.

import { graphql } from 'msw'
 
const github = graphql.link('https://api.github.com/graphql')
const stripe = graphql.link('https://api.stripe.com')
 
export const handlers = [
  github.query('GetUser', githubUserResolver),
  stripe.query('GetUser', stripeUserResolver),
]

了解有关 graphql.link() API 的更多信息。

¥Learn more about the graphql.link() API.

常见问题

¥Common questions

如何在 Node.js 中使用相对 URL?

¥How do I use relative URLs in Node.js?

你不需要。在 Node.js 中,没有什么可以参考的。如果你正在描述 Node.js 应用的网络行为,请使用你请求的绝对 URL。如果你将 MSW 与基于 Node.js 的测试运行器(如 Jest 或 Vitest)一起使用,请相应地配置这些运行器以支持相对 URL(即填充 document.baseURI 字符串)。

¥You don’t. In Node.js, there is nothing to be relative to. If you are describing a network behavior for a Node.js application, use the absolute URLs you are requesting. If you are using MSW with a Node.js-based test runner, like Jest or Vitest, configure those runners accordingly to support relative URLs (i.e. polyfill the document.baseURI string).

相关材料

¥Related materials