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的路由系统. 例子
随机推荐
- Vue环境搭建及node安装过程整理
一.nodejs的安装 Node.js安装包及源码下载地址为:https://nodejs.org/en/download/. 我们可以根据不同平台系统选择你需要的Node.js安装包.Node.js ...
- 【UOJ#435】【集训队作业2018】Simple Tree 分块+树链剖分
题目大意: 有一棵有根树,根为 1 ,点有点权.现在有 m 次操作,操作有 3 种:1 x y w ,将 x 到 y 的路径上的点点权加上 w (其中 w=±1w=±1 ):2 x y ,询问在 x ...
- 【bzoj4589】Hard Nim FWT+快速幂
题目大意:给你$n$个不大于$m$的质数,求有多少种方案,使得这$n$个数的异或和为$0$.其中,$n≤10^9,m≤10^5$. 考虑正常地dp,我们用$f[i][j]$表示前$i$个数的异或和为$ ...
- java异常层次机构图
以下内容转自:http://www.cnblogs.com/toSeeMyDream/p/5213610.html 在Java中,异常分为受检查的异常,与运行时异常. 两者都在异常类层次结构中.下面的 ...
- gephi
http://pan.baidu.com/share/link?shareid=382396&uk=1696102437
- Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)(略有修改)
对应项目的代码地址为:https://github.com/liuxiaoming7708/springboot-dubbo-parent Dubbo与Zookeeper.SpringMVC整合和使用 ...
- Zookeeper+ActiveMQ集群搭建
搭建三台虚拟机安装centos7.要提前安装好jdk环境 1.环境准备,搭建三台虚拟机ip分别是 192.168.192.130 192.168.192.131 192.168.192.134 Zoo ...
- 解析xml文件步骤 -- pullparser
1. 初始化一个xml的解析器 XmlPullParser parser = Xml.newPullParser(); 2. 设置解析器的参数 InputStream inputStream = th ...
- Mina的客户端
(一) package client; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.a ...
- Oracle VM VirtualBox技巧
配置文件 Linux 虚拟机配置文件分为两处. windows下: 1.用户名/.VirtualBox/ 这里面有2个配置文件: VirtualBox.xml 和 VirtualBox.xml-pre ...