您的位置:首页 > 其它

Fastify 系列教程一 (路由和日志)

2017-10-25 14:15 344 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: