路由处理器
路由处理器可以让您定义 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,希望您将编写更少的函数路由处理程序,而更多的是简写。让我们接下来讨论一下这些内容!