Fastify 系列教程一 (路由和日志)
Fastify 系列教程:
- Fastify 系列教程一 (路由和日志)
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
- Fastify 系列教程三 (验证、序列化和生命周期)
- Fastify 系列教程四 (请求对象、响应对象和插件)
介绍
Fastify是一个高度专注于以最少开销和强大的插件架构,为开发人员提供最佳体验的Web框架。
它受到了 Hapi 和 Express 的启发,是目前最快的 Node 框架之一。
Fastify 独特的将 JSON Schema 应用到请求时的 validation 和响应时的 serialization,作者写的 fast-json-stringify
包更是达到了 2x faster than JSON.stringify() 的神奇效果。
起步
安装
输入如下指令安装 fastify :
npm i fastify --save
第一个 fastify 应用
app.js:
const fastify = require('fastify')()
// 注册匹配到 '/' 的路由
fastify.get('/', function (request, reply) {
reply
.send({ text: 'hello fastify' })
})
// 监听 3030 端口
fastify.listen(3030, err => {
if (err) {
console.log(err)
}
})
在命令行输入 node app.js 启动服务,在浏览器访问 localhost:3030 就可以访问到了:
路由
使用 fastify.route(option)
注册一个路由:
fastify.route({
method: 'post',
url: '/home',
handler: function(request, reply){
reply.send({text: 'hello fastify'})
}
})
上述代码将匹配到访问 '/home' 的 post 请求。
详细的 option 配置如下:
method:
目前只支持 'DELETE'
, 'GET'
, 'HEAD'
, 'PATCH'
, 'POST'
, 'PUT'
和 'OPTIONS'
。也可以是一个数组:
fastify.route({
method: ['POST', 'PUT'],
url: '/put_and_post',
handler: function(request, reply){
reply.send({hello: 'world'})
}
})
此时使用 POST
或者 PUT
方式访问 /put_and_post
都能成功响应。
注意:Fastify 只支持
application/json
内容类型,如果想解析其他类型可以查看社区插件,fastify-formbody 可以解析x-www-form-urlencoded
请求类型,使用 fastify-multipart 处理form-data
类型请求。或者使用 ContentTypeParser 方法自定义内容类型解析器。
url:
路由匹配的路径,也可以使用 path
代替。
所支持的 url 格式可以查看:https://github.com/delvedor/find-my-way#supported-path-formats
schema:
一个包含请求和响应模式的对象。
需要符合 JSON Schema 的格式。
body
: 验证请求体, 必须是'POST'
或者'PUT'
请求。querystring
: 验证查询字符串。可以是一个完成的 JSON Schema 对象(符合{type: "object", properties: {...}}
)或者没有type
和properties
属性,而只有查询字符串列表。params
: 验证params
response
: 过滤并生成响应的 schema ,可以提升 10-20% 的吞吐量。headers
: 验证请求头。
使用 schema 是 Fastify 比其他框架都快的原因所在,如何理解这个 schema 呢?大家都知道,在 JSON 里面有 string
, number
, object
, array
, null
共五中类型,不同的类型格式是不一样的,比如字符串两头需要加引号,数组两头需要加括号等等,如果不用 schema 指明 json 是何种模式的话,原生的 JSON.stringify()
会先判断字段的类型后再做相应字符串化方法。而 JSON Schema 就是来定义 json 的数据格式的,通过 JSON Schema 可以达到验证 JSON 是否符合规定的格式。而框架作者独创的使用 JSON Schema 并手写 stringify
(fast-json-stringify) ,达到了 2x faster than JSON.stringify() ,也是令人敬佩不已,也是正如 贺师俊 老师评论的那样:
好了,言归正传,使用 schema 示例:
fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
name: { type: 'number' }
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
console.log(request.query)
reply.send({ hello: 'world' })
}
})
当访问 http://localhost:3030/put_and_post?name=2 时可以正常得到 {"hello": "world"}
,同时控制台也会输入 {name: 2}
,注意,name=2
这条查询字符串也被自动转成了 number
类型了。
如果访问 http://localhost:3030/put_and_post?name=true 会发生什么呢?对不起,Fastify 会返回 400
错误,因为 true
不是 number
类型,而是一个字符串:
{
"error": "Bad Request",
"message": [{
"keyword":"type",
"dataPath":".name",
"schemaPath":"#/properties/name/type",
"params":{"type":"number"},
"message":"should be number"
}],
"statusCode": 400
}
如果将 reply.send({hello: 'world'})
,改成 reply.send({text: 'world'})
会发生什么呢?
客户端将不会得到 text
这个字段,因为 {text: 'world'}
不符合定义的 responese
规则,最终只会得到 {}
。
beforeHandler(request, reply, done):
在处理请求之前调用的函数,可以在这里处理用户认证:
fastify.route({
method: 'POST',
path: '/authentication',
beforeHandler: function(request, reply, done){
if(request.body.username === 'lavyun') {
done()
} else {
reply.send({
meg: 'Authentication failed!',
code: '1'
})
}
},
handler: function(request, reply){
reply.send({
code: '0',
msg: 'Authentication success!'
})
}
})
handler(request, reply):
用来处理请求的函数。
schemaCompiler(schema):
schemaCompiler
是一个指定 schema 编译器的方法。(用来验证 body, params, headers, querystring)。默认的 schemaCompiler
返回一个实现 ajv
接口的编译器。
比如使用 Joi:
const Joi = require('joi')
fastify.post('/the/url', {
schema: {
body: Joi.object().keys({
hello: Joi.string().required()
}).required()
},
schemaCompiler: schema => data => Joi.validate(data, schema)
})
fastify 支持类似 Express/Restify
的路由语法:
fastify.get(path, [options], handler)
fastify.head(path, [options], handler)
fastify.post(path, [options], handler)
fastify.put(path, [options], handler)
fastify.delete(path, [options], handler)
fastify.options(path, [options], handler)
fastify.patch(path, [options], handler)
fastify.all(path, [options], handler)
示例:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (req, reply) => {
reply.send({ hello: 'world' })
})
Async Await
使用 async/await
:
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
return processed
})
可以看到,我们没有调用 reply.send
来返回数据给用户,而是使用 return
,当然你也可以使用 replay.send
:
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
reply.send(processed)
})
警告:如果同时使用
return
和reply.send
,则后者将会被舍弃。
路由前缀
有时我们会维护两套不同版本的api,一般我们都会手动的给所有的路由加上版本号前缀,例如:v1/user
。
Fastify 提供了快速而智能的方式来创建不同版本的相同的api,而无需手动更改所有路由名称:
// 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)
// routes/v1/users.js
module.exports = function (fastify, opts, next) {
fastify.get('/user', handler_v1)
next()
}
// routes/v2/users.js
module.exports = function (fastify, opts, next) {
fastify.get('/user', handler_v2)
next()
}
因为在编译时会自动处理前缀(这也意味着性能不会受到影响),所以Fastify不会抱怨两个不同的路由使用相同的名称。
现在客户端将可以访问以下路由:
/v1/user
/v2/user
日志
日志功能默认是关闭的,如果需要启用,可以这样做:
const fastify = require('fastify')({
logger: true
})
fastify.get('/', function (req, reply) {
reply.send({ hello: 'world' })
})
// 监听 3030 端口
fastify.listen(3030, err => {
if (err) {
console.log(err)
}
})
此时,当收到请求后,会在控制台打印日志:
{
"pid":78817,
"hostname":"localhost",
"level":30,
"time":1508910898897,
"msg":"incoming request",
"reqId":1,
"req":{
"id":1,
"method":"GET",
"url":"/",
"remoteAddress":"::1",
"remotePort":57731
},
"v":1
}
{
"pid":78817,
"hostname":"localhost",
"level":30,
"time":1508910898907,
"msg":"request completed",
"reqId":1,
"res":{
"statusCode":200
},
"responseTime":8.21057403087616,
"v":1
}
Fastify 的更多使用将在接下来的博客中说明。
Tips:
访问 https://lavyun.gitbooks.io/fastify/content/ 查看我翻译的 Fastify 中文文档。
访问我的个人博客查看我的最新动态 lavyun.cn
Fastify 系列教程一 (路由和日志)的更多相关文章
- Fastify 系列教程一(路由和日志)
介绍 Fastify是一个高度专注于以最少开销和强大的插件架构,为开发人员提供最佳体验的Web框架. 它受到了 Hapi 和 Express 的启发,是目前最快的 Node 框架之一. Fastify ...
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) 中间件 Fastify 提供了与 Express 和 Restify ...
- Fastify 系列教程三 (验证、序列化和生命周期)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) 验证 Fast ...
- Fastify 系列教程四 (求对象、响应对象和插件)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) Fastify ...
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) Fastify ...
- RabbitMQ系列教程之四:路由(Routing)(转载)
RabbitMQ系列教程之四:路由(Routing) (使用Net客户端) 在上一个教程中,我们构建了一个简单的日志系统,我们能够向许多消息接受者广播发送日志消息. 在本教程中,我们将为其添加一项功能 ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
- angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用
今天我们要讲的是ng2的路由系统. 例子
随机推荐
- mysql-mmm
查看mmm集群状态: mmm_control show 给主机设置ip: mmm_control set_ip ip host 改变状态: mmm_control set_passive|active ...
- ASP.NET MVC Forms验证机制
ASP.NET MVC 3 使用Forms身份验证 身份验证流程 一.用户登录 1.验证表单:ModelState.IsValid 2.验证用户名和密码:通过查询数据库验证 3.如果用户名和密码正确, ...
- 腾讯云 利用php + apache + mysql 搭建服务器环境
1.一键安装需要的软件源 yum install -y httpd php php-fpm mysql mysql-server php-mysql 1) httpd 即为 apache 2)php ...
- 平衡二叉树(AVL)的理解和实现(Java)
AVL的定义 平衡二叉树:是一种特殊的二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1.从平衡二叉树的名字中可以看出来,它是一种高度平衡的二叉排序树.那么什么叫做高度平衡呢?意思就是要么它 ...
- js03
我们接着来学习js的一些基础知识点. 1.document: document是window对象的一个属性.window对象表示浏览器中打开的窗口.如果文档包含框架(frame或者iframe),浏览 ...
- Impala配置HA-Nginx
Impala的高可用配置,官方的例子用的是Haproxy,考虑到nginx配置简单,使用人群广泛,再加上nginx1.9以后支持TCP的负载均衡,所以选用nginx. nginx安装:yum inst ...
- linux中的tar命令的使用
参考网址: https://www.cnblogs.com/newcaoguo/p/5896975.html https://www.cnblogs.com/xccjmpc/p/6035397.htm ...
- Ubuntu链接库查找
apt-get install apt-file apt-get update apt-file search
- Java学习之路(十一):IO流<前戏>
File类的概述和构造方法 构造方法: File(String pathname):根据一个路径得到File对象 File(String parent,String child):根据一个目录和一个子 ...
- spring boot的静态资源加载
1.spring boot默认资源处理 Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. spring boot默认加载文 ...