Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: 92e4e19...c872f0b #250

Merged
merged 1 commit into from Dec 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": false
}
4 changes: 2 additions & 2 deletions docs/ContentTypeParser.md
Expand Up @@ -83,15 +83,15 @@ fastify.addContentTypeParser('text/xml', function (request, payload, done) {
})

// 移除内建的 content type 解析器。这时只有上文添加的 text/html 解析器可用。
fastiy.removeContentTypeParser(['application/json', 'text/plain'])
Fastiy.removeContentTypeParser(['application/json', 'text/plain'])
```

#### removeAllContentTypeParsers

在上文的例子中,你需要明确指定所有你想移除的 content type。但你也可以使用 `removeAllContentTypeParsers`直接移除所有现存的 content type 解析器。在下面的例子里,我们实现了一样的效果,但不再需要手动指定 content type 了。和 `removeContentTypeParser` 一样,该 API 也支持封装。当你想注册一个[能捕获所有 content type 的解析器](#Catch-All),且忽略内建的解析器时,这个 API 特别有用。

```js
fastiy.removeAllContentTypeParsers()
Fastiy.removeAllContentTypeParsers()

fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
Expand Down
78 changes: 75 additions & 3 deletions docs/Getting-Started.md
Expand Up @@ -21,6 +21,13 @@ yarn add fastify
让我们开始编写第一个服务器吧:
```js
// 加载框架并新建实例

// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
Expand All @@ -36,13 +43,19 @@ fastify.listen(3000, function (err, address) {
fastify.log.error(err)
process.exit(1)
}
fastify.log.info(`server listening on ${address}`)
// 服务器监听地址:${address}
})
```

更喜欢使用 `async/await`?Fastify 对其提供了开箱即用的支持。<br>
*(我们还建议使用 [make-promises-safe](https://github.com/mcollina/make-promises-safe) 来避免文件描述符 (file descriptor) 及内存的泄露)*
```js
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
Expand Down Expand Up @@ -89,6 +102,26 @@ start()
在深入之前,先来看看插件系统是如何工作的吧!<br>
让我们新建一个基本的服务器,但这回我们把路由 (route) 的声明从入口文件转移到一个外部文件。(参阅[路由声明](Routes.md))。
```js
// ESM
import Fastify from 'fastify'
import firstRoute from './our-first-route'
const fastify = Fastify({
logger: true
})

fastify.register(firstRoute)

fastify.listen(3000, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// 服务器监听地址:${address}
})
```

```js
// CommonJs
const fastify = require('fastify')({
logger: true
})
Expand All @@ -100,7 +133,7 @@ fastify.listen(3000, function (err, address) {
fastify.log.error(err)
process.exit(1)
}
fastify.log.info(`server listening on ${address}`)
// 服务器监听地址:${address}
})
```

Expand Down Expand Up @@ -132,6 +165,28 @@ npm i --save fastify-plugin fastify-mongodb

**server.js**
```js
// ESM
import Fastify from 'fastify'
import dbConnector from './our-db-connector'
import firstRoute from './our-first-route'

const fastify = Fastify({
logger: true
})
fastify.register(dbConnector)
fastify.register(firstRoute)

fastify.listen(3000, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// 服务器监听地址:${address}
})
```

```js
// CommonJs
const fastify = require('fastify')({
logger: true
})
Expand All @@ -144,13 +199,30 @@ fastify.listen(3000, function (err, address) {
fastify.log.error(err)
process.exit(1)
}
fastify.log.info(`server listening on ${address}`)
// 服务器监听地址:${address}
})

```

**our-db-connector.js**
```js
// ESM
import fastifyPlugin from 'fastify-plugin'
import fastifyMongo from 'fastify-mongodb'

async function dbConnector (fastify, options) {
fastify.register(fastifyMongo, {
url: 'mongodb://localhost:27017/test_database'
})
}

// 用 fastify-plugin 包装插件,以使插件中声明的装饰器、钩子函数暴露在根作用域里。
module.exports = fastifyPlugin(dbConnector)

```

```js
// CommonJs
const fastifyPlugin = require('fastify-plugin')

async function dbConnector (fastify, options) {
Expand Down
34 changes: 34 additions & 0 deletions docs/Hooks.md
Expand Up @@ -543,3 +543,37 @@ fastify.route({
```

**注**:两个选项都接受一个函数数组作为参数。

## 诊断通道钩子

> **注:** `诊断通道` (diagnostics_channel) 是当前 Node.js 的试验特性,
> 因此,其 API 即便在补丁版本中也可能会发生变动。
> 对于 Fastify 支持的 Node.js 版本,不兼容 `诊断通道` 的,
> 将会使用 [polyfill](https://www.npmjs.com/package/diagnostics_channel)。
> 而 polyfill 都不支持的版本将无法使用该特性。

当前版本在初始化时,会有一个[`诊断通道`](https://nodejs.org/api/diagnostics_channel.html)发布 `'fastify.initialization'` 事件。此时,Fastify 的实例将会作为回调函数参数的一个属性,该实例可以添加钩子、插件、路由及其他任意内容。

举例来说,某个监控工具可以如下使用(当然这是一个简化的例子)。在典型的“探测工具优先加载 (require instrumentation
tools first)”风格下,这段代码会在被监控的应用初始化时加载。

```js
const tracer = /* 某个监控工具 */
const dc = require('diagnostics_channel')
const channel = dc.channel('fastify.initialization')
const spans = new WeakMap()

channel.subscribe(function ({ fastify }) {
fastify.addHook('onRequest', (request, reply, done) => {
const span = tracer.startSpan('fastify.request')
spans.set(request, span)
done()
})

fastify.addHook('onResponse', (request, reply, done) => {
const span = spans.get(request)
span.finish()
done()
})
})
```
2 changes: 1 addition & 1 deletion docs/Lifecycle.md
Expand Up @@ -19,7 +19,7 @@ Incoming Request
4**/5** ◀─┴─▶ preValidation Hook
415 ◀─┴─▶ Validation
400 ◀─┴─▶ Validation
4**/5** ◀─┴─▶ preHandler Hook
Expand Down
81 changes: 80 additions & 1 deletion docs/Recommendations.md
Expand Up @@ -27,7 +27,9 @@ Fastify 团队**强烈**地认为上述做法是一种反面模式,是非常
1. 应用需要处理多域名。
1. 应用需要处理静态资源,例如 jpeg 文件。

反向代理的解决方案有很多种,例如 AWS 与 GCP,具体根据环境来择用。对于上述的案例,我们可以使用 [HAProxy][haproxy]。
反向代理的解决方案有很多种,例如 AWS 与 GCP,具体根据环境来择用。对于上述的案例,我们可以使用 [HAProxy][haproxy] 或 [Nginx][nginx]。

### HAProxy

```conf
# global 定义了 HAProxy 实例的基础配置。
Expand Down Expand Up @@ -134,6 +136,83 @@ backend static-backend
[why-use]: https://web.archive.org/web/20190821102906/https://medium.com/intrinsic/why-should-i-use-a-reverse-proxy-if-node-js-is-production-ready-5a079408b2ca
[haproxy]: https://www.haproxy.org/

### Nginx

```nginx
upstream fastify_app {
# 更多信息请见:http://nginx.org/en/docs/http/ngx_http_upstream_module.html
server 10.10.11.1:80;
server 10.10.11.2:80;
server 10.10.11.3:80 backup;
}

server {
# 默认服务器
listen 80 default_server;
listen [::]:80 default_server;

# 指定端口
# listen 80;
# listen [::]:80;
# server_name example.tld;

location / {
return 301 https://$host$request_uri;
}
}

server {
# 默认服务器
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;

# 指定端口
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name example.tld;

# 密钥
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/private.pem;
ssl_trusted_certificate /path/to/chain.pem;

# 通过 https://ssl-config.mozilla.org/ 生成最佳配置
ssl_session_timeout 1d;
ssl_session_cache shared:FastifyApp:10m;
ssl_session_tickets off;

# 现代化配置
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;

# HTTP 严格传输安全 (HSTS) (需要 ngx_http_headers_module 模块) (63072000 秒)
add_header Strict-Transport-Security "max-age=63072000" always;

# 在线证书状态协议缓存 (OCSP stapling)
ssl_stapling on;
ssl_stapling_verify on;

# 自定义域名解析器 (resolver)
# resolver 127.0.0.1;

location / {
# 更多信息请见:http://nginx.org/en/docs/http/ngx_http_proxy_module.html
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_pass http://fastify_app:3000;
}
}
```

[nginx]: https://nginx.org/

## Kubernetes
<a id="kubernetes"></a>

Expand Down
2 changes: 1 addition & 1 deletion docs/Reply.md
Expand Up @@ -339,7 +339,7 @@ fastify.get('/', {
})
```

如果你想完全自定义错误处理,请看 [`setErrorHandler`](Server.md#seterrorhandler) API。<br>
如果你想自定义错误处理,请看 [`setErrorHandler`](Server.md#seterrorhandler) API。<br>
*注:当自定义错误处理时,你需要自行记录日志*

API:
Expand Down
6 changes: 3 additions & 3 deletions docs/Request.md
Expand Up @@ -3,18 +3,18 @@
## Request
处理函数的第一个参数是 `Request`.<br>
Request 是 Fastify 的核心对象,包含了以下字段:
- `query` - 解析后的 querystring
- `query` - 解析后的 querystring,其格式由 [`querystringParser`](Server.md#querystringParser) 指定。
- `body` - 消息主体
- `params` - URL 参数
- [`headers`](#headers) - header 的 getter 与 setter
- `raw` - Node 原生的 HTTP 请求
- `req` *(不推荐,请使用 `.raw`)* - Node 原生的 HTTP 请求
- `server` - Fastify 服务器的实例,以当前的[封装上下文](Encapsulation.md)为作用域。
- `id` - 请求 id
- `id` - 请求 ID
- `log` - 请求的日志实例
- `ip` - 请求方的 ip 地址
- `ips` - x-forwarder-for header 中保存的请求源 ip 数组,按访问先后排序 (仅当 [`trustProxy`](Server.md#factory-trust-proxy) 开启时有效)
- `hostname` - 请求方的主机名 (当 [`trustProxy`](Server.md#factory-trust-proxy) 启用时,从 `X-Forwarded-Host` header 中获取)
- `hostname` - 请求方的主机名 (当 [`trustProxy`](Server.md#factory-trust-proxy) 启用时,从 `X-Forwarded-Host` header 中获取)。为了兼容 HTTP/2,当没有相关 header 存在时,将返回 `:authority`。
- `protocol` - 请求协议 (`https` 或 `http`)
- `method` - 请求方法
- `url` - 请求路径
Expand Down