路由处理器

路由处理器可以让您定义 Mirage 服务器可以处理的 URL。

最简单的路由处理器将 URL 映射到一个对象。

this.get("/movies", { movies: ["Interstellar", "Inception", "Dunkirk"] })

现在,当您的应用程序向 /movies 发出 GET 请求时,它将收到此对象作为 JSON 数据。

如果您的 API 与应用程序在不同的主机或端口上,请设置 urlPrefix

    routes() {
      this.urlPrefix = 'https://127.0.0.1:3000';

您也可以通过将函数作为第二个参数传递来编写函数路由处理器。

this.get("/movies", (schema, request) => {
  return ["Interstellar", "Inception", "Dunkirk"]
})

函数路由处理器是编写路由处理器的最灵活的方式,因为它们让您能够访问 Mirage 的数据层和请求对象。您的大多数路由处理器将是函数。

您可以使用任何 HTTP 动词来定义您的 API 路由。每个动词方法都有相同的签名。第一个参数是路径 (URL),第二个参数是一个返回响应的函数。

this.get('/movies', () => { ... });
this.post('/movies', () => { ... });
this.patch('/movies/:id', () => { ... });
this.put('/movies/:id', () => { ... });
this.del('/movies/:id', () => { ... });
this.options('/movies', () => { ... });

计时

路由处理器的最后一个参数是一个选项对象,您可以使用它来调整计时。使用此选项来延迟特定路由的响应,并查看您的应用程序在与缓慢的网络通信时的行为。

this.get(
  "/movies",
  () => {
    return ["Interstellar", "Inception", "Dunkirk"]
  },
  { timing: 4000 }
)

默认情况下,开发期间的延迟为 400 毫秒,测试期间的延迟为 0(这样您的测试运行速度更快)。

您还可以为所有路由设置一个全局计时参数。单个计时参数会覆盖全局设置。

createServer({
  routes() {
    this.namespace = "api"
    this.timing = 2000

    this.get("/movies", () => {
      return ["Interstellar", "Inception", "Dunkirk"]
    })

    this.get(
      "/complex-query",
      () => {
        return [1, 2, 3, 4, 5]
      },
      { timing: 3000 }
    )
  },
})

如果您想在测试中添加延迟,可以通过将计时参数放在测试中来覆盖单个测试的计时。

test("this route works with a delay", function () {
  server.timing = 10000

  // ...
})

因为服务器在每次测试后都会重置,所以此选项不会泄漏到您的套件的其余部分。

访问数据层

路由处理器接收 schema 作为其第一个参数,它使它们能够访问 Mirage 的数据层。

this.get("/movies", (schema) => {
  return schema.movies.all()
})

您的大多数路由处理器将以某种方式与数据层进行交互。

第二个参数是 request 对象,它包含有关应用程序发出的请求的信息。例如,您可以从中访问动态 URL 段。

this.get("/movies/:id", (schema, request) => {
  let id = request.params.id

  return schema.movies.find(id)
})

您还可以访问请求主体,例如处理包含应用程序发送过来的数据的 POST 或 PATCH 请求。

this.post("/movies", (schema, request) => {
  let attrs = JSON.parse(request.requestBody)

  return schema.movies.create({ attrs })
})

normalizedRequestAttrs 帮助器(在下面有记录)为处理请求数据提供了一些辅助功能。

动态路径和查询参数

注入到路由处理器的请求对象包含任何动态路由段和查询参数。

要定义具有动态段的路由,请在路径中使用冒号语法 (:segment)。动态部分将通过 request.params.[segment] 提供。

this.get("/authors/:id", (schema, request) => {
  let id = request.params.id

  return schema.authors.find(id)
})

请求中的查询参数也可以通过 request.queryParams.[param] 访问。

状态代码和头部

默认情况下,Mirage 根据用于路由的动词来设置响应的 HTTP 状态代码。

  • GET 为 200。
  • PATCH/PUT 为 204。
  • POST 为 201。
  • DEL 为 204。

如果存在响应主体,PATCH/PUT 和 POST 会更改为 200。

此外,Content-Type 的头部被设置为 application/json

您可以通过在路由处理程序中返回 Response 类的实例来自定义响应代码和头部。

import { createServer, Model, Response } from "miragejs"

createServer({
  models: {
    author: Model,
  },
  routes() {
    this.post("/authors", function (schema, request) {
      let attrs = JSON.parse(request.requestBody).author

      if (attrs.name) {
        return schema.authors.create(attrs)
      } else {
        return new Response(
          400,
          { some: "header" },
          { errors: ["name cannot be blank"] }
        )
      }
    })
  },
})

外部来源

您可以使用 Mirage 模拟跨域请求。默认情况下,像这样的路由

this.get('/contacts', ...)

将拦截对与您的应用程序相同的域的请求。要处理不同的域,请使用完全限定域名

this.get('http://api.twitter.com/v1', ...)

如果您的整个应用程序使用外部(跨域)API,您可以通过 urlPrefix 全局配置域。

createServer({
  routes() {
    this.urlPrefix = 'https://my.api.com';

    // This route will intercept requests to https://my.api.com/contacts
    this.get('/contacts', ...)
  }
})

帮助器

在编写函数路由处理程序时,可以使用一些辅助程序。

如果您不熟悉 Mirage,不用担心现在不理解它们。它们在编写更高级的路由处理程序时很有用。

序列化

此辅助程序在将给定的模型或集合传递给序列化器层后返回其 JSON。如果您想在返回之前对序列化后的 JSON 进行一些最终处理,它会很有用。

// Note: Be sure to use function() here, rather than () => {}
this.get("/movies", function (schema) {
  let movies = schema.movies.all()
  let json = this.serialize(movies)

  json.meta = { page: 1 }

  return json
})

默认情况下,此方法使用给定模型或集合的命名序列化器。您可以将特定的序列化器名称作为第二个参数传入

this.get("/movies", function (schema) {
  let movies = schema.movies.all()
  let json = this.serialize(movies, "sparse-movie")

  json.meta = { page: 1 }

  return json
})

您将在这些指南的后面学习更多关于序列化器的内容。

规范化请求属性

此辅助程序以规范化形式返回请求的正文,适合用于处理和创建记录。它基本上从 API 负载中删除了格式化逻辑,为您提供底层属性,然后您可以使用这些属性修改 Mirage 的数据库。

例如,如果您的应用程序使用以下数据发出 POST 请求

// POST /users

{
  "data": {
    "type": "users",
    "attributes": {
      "first-name": "Conan",
      "middle-name": "the",
      "last-name": "Barbarian"
    },
    "relationships": {
      "team": {
        "data": {
          "type": "teams",
          "id": 1
        }
      }
    }
  }
}

然后 normalizedRequestAttrs() 可以像这样使用

this.post("/users", function (schema, request) {
  let attrs = this.normalizedRequestAttrs()
  /*
    attrs is this object:
      {
        firstName: 'Conan',
        middleName: 'the',
        lastName: 'Barbarian',
        teamId: '1'
      }
  */
  return schema.users.create(attrs)
})

请注意,属性键是驼峰式的,team 外键被提取出来。这是因为 user 拥有 team 外键;如果请求中包含其他关系,但 user 不拥有其外键,它将不会被提取。

此辅助方法利用了您的序列化器的 normalize 方法。在上面的示例中,假设应用程序使用的是 JSONAPISerializer,它附带了已编写的 #normalize 方法。如果您没有使用捆绑的序列化器之一,您需要实现 #normalize 并让它返回一个 JSON:API 文档来利用此方法。

此外,您需要在这里使用完整的 function,而不是 ES6 箭头函数(例如 () => { ... })。这是因为 normalizedRequestAttrs 需要函数处理程序的 this 上下文,而箭头函数将绑定外部范围的 this

normalizedRequestAttrs() 依赖于 modelName 来工作,并尝试根据请求的 URL 自动检测它。如果您使用的是传统 URL - 例如 PATCH /users/1 - 则辅助程序应该可以工作。如果您正在使用一些自定义的 - 例如 PATCH /users/edit/1 - 您需要将 modelName 作为第一个参数提供给辅助程序

this.patch("/users/edit/:id", function (schema, request) {
  let attrs = this.normalizedRequestAttrs("user")
  // ...
})

关于编写低级函数路由处理程序的内容就这些了!

函数路由处理程序很灵活,但对于每个端点来说,编写它们都很麻烦。如果您正在使用一个足够传统的 API,希望您将编写更少的函数路由处理程序,而更多的是简写。让我们接下来讨论一下这些内容!