|
|
|
|
> 更新比较慢的版本,先熟悉这个教程和这个框架
|
|
|
|
|
> [fastify/docs-chinese: Fastify 中文文档 (github.com)](https://github.com/fastify/docs-chinese)
|
|
|
|
|
|
|
|
|
|
# 开始
|
|
|
|
|
|
|
|
|
|
准备
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm init -y
|
|
|
|
|
# 运行处加入 "build": "tsc -p tsconfig.json",
|
|
|
|
|
npx tsc --init
|
|
|
|
|
pnpm i -S fastify
|
|
|
|
|
pnpm i -D typescript @types/node
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// started.ts
|
|
|
|
|
import Fastify from 'fastify'
|
|
|
|
|
|
|
|
|
|
const fastify = Fastify({
|
|
|
|
|
logger: false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
fastify.get('/', async (request, reply) => {
|
|
|
|
|
return "Hello World"
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async function start(){
|
|
|
|
|
try{
|
|
|
|
|
await fastify.listen({
|
|
|
|
|
port:3000,
|
|
|
|
|
host:'0.0.0.0'
|
|
|
|
|
})
|
|
|
|
|
}catch (e){
|
|
|
|
|
fastify.log.error(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
运行
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm run build
|
|
|
|
|
node started.js
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# 路由
|
|
|
|
|
|
|
|
|
|
## 完整格式
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.route({
|
|
|
|
|
method: 'GET',// 请求类型
|
|
|
|
|
url: '/',// 请求地址
|
|
|
|
|
schema: {// 请求参数验证
|
|
|
|
|
querystring: {
|
|
|
|
|
name: { type: 'string' },
|
|
|
|
|
excitement: { type: 'integer' }
|
|
|
|
|
},
|
|
|
|
|
response: {// 响应参数验证
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
hello: { type: 'string' }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handler: function (request, reply) {// 响应程序
|
|
|
|
|
reply.send({ hello: 'world' })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 简写格式
|
|
|
|
|
|
|
|
|
|
- 也可以将处理函数卸载opts里面,但是如果简写中和opts里面都写了处理函数会报错
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const opts = {
|
|
|
|
|
schema: {
|
|
|
|
|
response: {
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
hello: { type: 'string' }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fastify.get('/', opts, (request, reply) => {
|
|
|
|
|
reply.send({ hello: 'world' })
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## URL构建
|
|
|
|
|
|
|
|
|
|
- Fastify 同时支持静态与动态的 URL
|
|
|
|
|
- 要注册一个**参数命名**的路径,请在参数名前加上_冒号_。_星号_表示**通配符**。 _注意,静态路由总是在参数路由和通配符之前进行匹配_
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 参数路由
|
|
|
|
|
fastify.get('/example/:userId', (request, reply) => {})
|
|
|
|
|
fastify.get('/example/:userId/:secretToken', (request, reply) => {})
|
|
|
|
|
|
|
|
|
|
// 通配符
|
|
|
|
|
fastify.get('/example/*', (request, reply) => {})
|
|
|
|
|
// 多参数
|
|
|
|
|
fastify.get('/example/near/:lat-:lng/radius/:r', (request, reply) => {})
|
|
|
|
|
// 真实的冒号:
|
|
|
|
|
fastify.post('/name::verb') // 将被解释为 /name:verb
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Async, Await
|
|
|
|
|
|
|
|
|
|
- 可以直接使用return返回值
|
|
|
|
|
- 假如在路由中,`reply.send()` 脱离了 promise 链,在一个基于回调的 API 中被调用,你可以使用 `await reply`, 也可以是`return reply`
|
|
|
|
|
- - `return value` 与 `reply.send(value)`同时使用只会发送第一次,同时还会触发警告日志,因为你试图发送两次响应。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.get('/', options, async function (request, reply) {
|
|
|
|
|
setImmediate(() => {
|
|
|
|
|
reply.send({ hello: 'world' })
|
|
|
|
|
})
|
|
|
|
|
await reply
|
|
|
|
|
// return reply
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 路由前缀
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// server.js
|
|
|
|
|
const fastify = require('fastify')()
|
|
|
|
|
|
|
|
|
|
fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
|
|
|
|
|
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
|
|
|
|
|
|
|
|
|
|
fastify.listen(3000)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// routes/v1/users.js
|
|
|
|
|
module.exports = function (fastify, opts, done) {
|
|
|
|
|
fastify.get('/user', handler_v1)
|
|
|
|
|
done()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// routes/v2/users.js
|
|
|
|
|
module.exports = function (fastify, opts, done) {
|
|
|
|
|
fastify.get('/user', handler_v2)
|
|
|
|
|
done()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 自定义日志级别
|
|
|
|
|
|
|
|
|
|
- 可以在统一路由加入打印固定字段的,但是非必要
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// server.js
|
|
|
|
|
const fastify = require('fastify')({ logger: true })
|
|
|
|
|
|
|
|
|
|
fastify.register(require('./routes/user'), { logLevel: 'warn' })
|
|
|
|
|
fastify.register(require('./routes/events'), { logLevel: 'debug' })
|
|
|
|
|
|
|
|
|
|
fastify.listen(3000)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- 也可以直接在路由的opts中定义
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
|
|
|
|
|
reply.send({ hello: 'world' })
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# 装饰器
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.decorate('utility', function () {
|
|
|
|
|
// 新功能的代码
|
|
|
|
|
})
|
|
|
|
|
fastify.decorate('conf', {
|
|
|
|
|
db: 'some.db',
|
|
|
|
|
port: 3000
|
|
|
|
|
})
|
|
|
|
|
// 通过装饰属性的名称便可访问值:
|
|
|
|
|
fastify.utility()
|
|
|
|
|
console.log(fastify.conf.db)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
路由函数的 `this` 指向 Fastify server
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.decorate('db', new DbConnection())
|
|
|
|
|
|
|
|
|
|
fastify.get('/', async function (request, reply) {
|
|
|
|
|
reply({hello: await this.db.query('world')})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 检查装饰器的存在
|
|
|
|
|
|
|
|
|
|
用于检查服务器实例上是否存在某个装饰器:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 用于检查服务器实例上是否存在某个装饰器:
|
|
|
|
|
fastify.hasDecorator('utility')
|
|
|
|
|
// 用于检查 Request 实例上是否存在某个装饰器
|
|
|
|
|
fastify.hasRequestDecorator('utility')
|
|
|
|
|
// 用于检查 Reply 实例上是否存在某个装饰器
|
|
|
|
|
fastify.hasReplyDecorator('utility')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# 验证和序列化
|
|
|
|
|
|
|
|
|
|
## 验证器复用
|
|
|
|
|
|
|
|
|
|
- `myField: { $ref: '#foo'}` 将在当前 schema 内搜索 `$id: '#foo'` 字段。
|
|
|
|
|
- `myField: { $ref: '#/definitions/foo'}` 将在当前 schema 内搜索 `definitions.foo` 字段。
|
|
|
|
|
- `myField: { $ref: 'http://url.com/sh.json#'}` 会搜索含 `$id: 'http://url.com/sh.json'` 的共用 schema。
|
|
|
|
|
- `myField: { $ref: 'http://url.com/sh.json#/definitions/foo'}` 会搜索含 `$id: 'http://url.com/sh.json'` 的共用 schema,并使用其 `definitions.foo` 字段。
|
|
|
|
|
- `myField: { $ref: 'http://url.com/sh.json#foo'}` 会搜索含 `$id: 'http://url.com/sh.json'` 的共用 schema,并使用其内部带 `$id: '#foo'` 的对象。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.addSchema({
|
|
|
|
|
$id: 'http://example.com/',
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
hello: { type: 'string' }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
fastify.post('/', {
|
|
|
|
|
handler () {},
|
|
|
|
|
schema: {
|
|
|
|
|
body: {
|
|
|
|
|
type: 'array',
|
|
|
|
|
items: { $ref: 'http://example.com#/properties/hello' }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// 作为根引用
|
|
|
|
|
fastify.addSchema({
|
|
|
|
|
$id: 'commonSchema',
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
hello: { type: 'string' }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
fastify.post('/', {
|
|
|
|
|
handler () {},
|
|
|
|
|
schema: {
|
|
|
|
|
body: { $ref: 'commonSchema#' },
|
|
|
|
|
headers: { $ref: 'commonSchema#' }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 获取共用Schema
|
|
|
|
|
|
|
|
|
|
- 当自定义验证器或序列化器的时候,Fastify 不再能控制它们,此时 `.addSchema` 方法失去了作用
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
fastify.addSchema({
|
|
|
|
|
$id: 'schemaId',
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
hello: { type: 'string' }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const mySchemas = fastify.getSchemas()
|
|
|
|
|
const mySchema = fastify.getSchema('schemaId')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 验证
|
|
|
|
|
|