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 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) 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的路由系统. 例子
随机推荐
- 201521123105 第10周Java学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-21.1 截图你的提交结果(出现 ...
- 201521123007《Java程序设计》第11周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ...
- Java:设计类的继承关系时的技巧
继承设计的技巧: (1)将公共操作和域放置在超类 (2)不要使用受保护的域 有些程序员认为,将大多数的实例域定义为protected是一个不错的主意,只有这样,子类才能够在需要的时候直接访问他们.然而 ...
- 在Ubuntu中部署并测试Fabric 1.0 Beta
[更新:1.0Beta已经是过去式了,现在出了1.0.0的正式版,请大家参照 http://www.cnblogs.com/studyzy/p/7437157.html 安装Fabric 1.0.0 ...
- shell脚本之算术运算和逻辑运算
目录 算术运算 赋值运算 逻辑运算 短路运算和异或 条件测试 数值测试 字符串测试 文件及其属性测试 存在性测试 存在性及类别测试 文件权限测试 文件特殊权限测试 文件大小测试 文件是否打开 双目测试 ...
- java.lang.RuntimeException: java.sql.SQLException: Too many parameters: expected 0, was given 1 Quer
如果出现类似这样的错误,检查一下是否sql语句和参数的位置对调了. java.lang.RuntimeException: java.sql.SQLException: Too many parame ...
- Mysql中设置默认时间为当前值
1.直接在创建表时添加该列并声明默认值,如下: CREATE TABLE `table1` ( `id` ) NOT NULL, `createtime` timestamp NULL default ...
- java围棋游戏源代码
//李雨泽源代码,不可随意修改.//时间:2017年9月22号.//地点:北京周末约科技有限公司.//package com.bao; /*围棋*/ /*import java.awt.*; impo ...
- 运行Chromium浏览器缺少google api密钥无法登录谷歌账号的解决办法
管理员身份运行CMD,然后依次输入以下三行内容: setx GOOGLE_API_KEY "AIzaSyAUoSnO_8k-3D4-fOp-CFopA_NQAkoVCLw"setx ...
- Linux命令行与脚本编程大全第一章
1, 2,linux内核:内存管理.进程管理.文件管理.设备管理. 其中内存管理如下图: 通过命令 cat/proc/meminfo查看系统的内存状态.通过ipcs查看共享内存.信号量.消息队列信息. ...