Fastify 系列教程二 (中间件、钩子函数和装饰器)
Fastify 系列教程:
- Fastify 系列教程一 (路由和日志)
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
- Fastify 系列教程三 (验证、序列化和生命周期)
- Fastify 系列教程四 (请求对象、响应对象和插件)
中间件
Fastify 提供了与 Express 和 Restify 中间件兼容的异步中间件引擎。
所以,Fastify 可以使用 Express 中间件。
注意:Fastify 中没有错误处理中间件(err, req, res, next)。如果需要处理中间件中的错误时,只需要调用 next(new Error('error message')) 即可,Fastify 会为你关闭请求并发送错误响应。
示例:
访问静态资源
配置 serve-static 访问静态资源。
目录结构:
public
|-css
|-js
|-jquery.js
|-images
views
app.js
app.js:
const serveStatic = require('serve-static')
const path = require('path')
fastify.use('/public', serveStatic(path.resolve(__dirname, 'public')))
此时,访问 localhost:3030/public/js/jquery.js 可以正确获得静态资源文件。
注意:中间件的请求和响应参数是原生 Nodejs Http 的
req和res,而路由中的request和reply是经过 Fastify 封装的,两者并不一样:
const fastify = require('fastify')()
fastify.use(function(req, res, next){
req.hello = "req.hello"
res.hello = "res.hello"
next()
})
fastify.use(function(req, res, next){
console.log('middle:', req.hello)
console.log('middle:', res.hello)
next()
})
fastify.get('/', function (request, reply) {
console.log('route:', request.hello)
console.log('route:', reply.hello)
reply.send({ hello: 'world' })
})
/*
middle: req.hello
middle: res.hello
route: undefined
route: undefined
*/
钩子函数
Fastify 中共有四个应用级钩子和一个路由级钩子。
四个应用级钩子:
onRequest(req, res, next)preHandler(request, reply, next)onResponse(res, next)onClose(instance, done)
一个路由级钩子:
beforeHandler(request, reply, done)
注意 req、res 和 request、reply 的区别。
| 参数 | 描述 |
|---|---|
| req | Node.js Http 请求对象 |
| res | Node.js Http 响应对象 |
| request | Fastify Request 接口 |
| reply | Fastify Reply 接口 |
| next | 继续下一个 生命周期 任务 |
使用 addHook 方法添加钩子函数:
fastify.addHook('onRequest', (req, res, next) => {
// some code
next()
})
fastify.addHook('preHandler', (request, reply, next) => {
// some code
next()
})
fastify.addHook('onResponse', (res, next) => {
// some code
next()
})
如果在执行钩子函数时遇到错误,只需将其传递给 next(),并且 Fastify 将自动关闭请求并向用户发送相应的错误代码。
fastify.addHook('onRequest', (req, res, next) => {
next(new Error('some error'))
})
如果你希望传递一个自定义状态码,可以使用reply.code():
fastify.addHook('preHandler', (request, reply, next) => {
reply.code(500)
next(new Error('some error'))
})
onClose
onClose 是唯一不在生命周期中的钩子,当调用 fastify.close() 来停止服务器时,会触发此钩子,第一个参数是 Fastify 实例,第二个用来完成回调。
const fastify = require('fastify')()
fastify.get('/close', function(request, reply){
reply.type('text/html').send('<h1>Close Server</h1>')
fastify.close()
})
fastify.onClose(function(instance, done){
console.log('close db connection')
done()
})
访问 /close 时页面会显示 Close Server,并且控制台会输出:
[Running] node "/Users/lavyun/Code/node/learn-fastify/app.js"
close db connection
[Done] exited with code=0 in 8.524 seconds
在关闭数据库链连接之后,app.js 也被 exit 了。
preHandler 和 beforeHandler
preHandler 的受众对象是所有的路由,而 beforeHandler 的受众对象是某个特定的路由,另外,beforeHandler 总是在 preHandler 之后执行。
fastify.addHook('preHandler', (request, reply, done) => {
console.log('preHandler')
done()
})
fastify.get('/', {
beforeHandler: function (request, reply, done) {
console.log('beforeHandler')
done()
}
}, function (request, reply) {
reply.send({ hello: 'world' })
})
// preHandler
// beforeHandler
beforeHandler 也可以是一个函数数组:
fastify.addHook('preHandler', (request, reply, done) => {
console.log('preHandler')
done()
})
const firstBeforeHandler = (request, reply, done) => {
console.log('first beforeHandler')
done()
}
const secondBeforeHandler = (request, reply, done) => {
console.log('second beforeHandler')
done()
}
fastify.get('/', {
beforeHandler: [firstBeforeHandler, secondBeforeHandler]
}, function (request, reply) {
reply.send({ hello: 'world' })
})
// preHandler
// first beforeHandler
// second beforeHandler
装饰器
如果想为 Fastify 实例添加功能,可以使用 decorate 方法。
decorate 允许向 Fastify 实例添加新属性。可以是一个值、一个函数,也可以是一个对象或一个字符串等。
使用方法
decorate
只需要调用 decorate 函数,并且传入新属性的键和值即可。
fastify.decorate('utility', () => {
// something very useful
})
也可以定义其他类型的实例:
fastify.decorate('conf', {
db: 'some.db',
port: 3000
})
一旦定义了这个实例,可以通过传入的参数名称来得到该值:
fastify.utility()
console.log(fastify.conf.db)
装饰器不可以重新被覆盖,如果要定义一个已经存在的装饰器,decorate 将会抛出异常。
fastify.decorate("d1", 'd1')
fastify.decorate("d1", "d2") // Error
decorateReply
decorateReply 可以为 Reply 对象添加新属性。
fastify.decorateReply('utility', function () {
// something very useful
})
decorateRequest
decorateRequest 可以为 Request 对象添加新属性。
fastify.decorateRequest('utility', function () {
// something very useful
})
extendServerError
如果要扩展 服务器错误,可以使用此 API,必须传入一个返回值为对象的函数,该函数将接收原始的 Error 对象,并返回新Error 对象来扩展服务器错误。
fastify.extendServerError((err) => {
return {
timestamp: new Date()
}
})
/*
最终的错误对象格式:
{
error: String
message: String
statusCode: Number
timestamp: Date
}
*/
依赖
如果一个装饰器依赖于另一个装饰器,可以将其他装饰器声明为依赖。只需要添加一个字符串数组(表示所依赖的装饰器的名称)作为第三个参数即可:
fastify.decorate('utility', fn, ['greet', 'log'])
如果不满足依赖关系,那么 decorate 会抛出一个异常,但是不用担心:依赖关系检查会在服务器启动之前执行,所以在运行时不会发生错误。
hasDecorator
可以使用 hasDecorator 检查装饰器是否存在:
fastify.hasDecorator('utility')
Fastify 的更多使用将在接下来的博客中说明。
Tips:
访问 https://lavyun.gitbooks.io/fastify/content/ 查看我翻译的 Fastify 中文文档。
访问 lavyun.cn 查看我的个人动态。
Fastify 系列教程二 (中间件、钩子函数和装饰器)的更多相关文章
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) 中间件 Fastify 提供了与 Express 和 Restify ...
- 【Flask】 python学习第一章 - 4.0 钩子函数和装饰器路由实现 session-cookie 请求上下文
钩子函数和装饰器路由实现 before_request 每次请求都会触发 before_first_requrest 第一次请求前触发 after_request 请求后触发 并返回参数 tear ...
- python---基础知识回顾(二)(闭包函数和装饰器)
一.闭包函数: 闭包函数: 1.在一个外函数中定义了一个内函数 2.内函数里运用了外函数的临时变量,而不是全局变量 3.并且外函数的返回值是内函数的引用.(函数名,内存块地址,函数名指针..) 正确形 ...
- Fastify 系列教程一(路由和日志)
介绍 Fastify是一个高度专注于以最少开销和强大的插件架构,为开发人员提供最佳体验的Web框架. 它受到了 Hapi 和 Express 的启发,是目前最快的 Node 框架之一. Fastify ...
- Fastify 系列教程三 (验证、序列化和生命周期)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) 验证 Fast ...
- Fastify 系列教程四 (求对象、响应对象和插件)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) Fastify ...
- Fastify 系列教程一 (路由和日志)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) Fastify ...
- CRL快速开发框架系列教程二(基于Lambda表达式查询)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- NGUI系列教程二
接下来我们创建一个Label,NGUI->Open the Widget Wizard,打开widgetTool对话框,在Template中选择Label,确定AddTo右侧选项为panel,点 ...
随机推荐
- 日志分析与splunk浅谈
难易程度:★★★ 阅读点:linux;python;web安全;日志分析; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 linux下的日志分析对企业来说非常重要,对我们分析p ...
- iOS-AFNetworking参数和多文件同时上传【多文件上传】
1. 前言 在项目开发中,我们经常需要上传文件,例如:上传图片,上传各种文件,而有时也需要将参数和多个文件一起上传,不知道大家的项目中遇到了没有,我在最近的项目中,就需要这样的一个功能:同时上传参数. ...
- Go语言学习笔记(2)——变量、常量
1. 变量(变量由字母.数字和下划线组成,且不能以数字开头!) 1.1 第一种 var name type // 未赋值时使用默认值 name = value 1.2 第二种: 根据所赋的值 ...
- 设置User Agent
公司的前端要给项目的webview加一个区分,用来区别是iOS端访问.android访问还是在浏览器访问的,说是要加一个User Agent ,前端根据不同信息做适配,和我说来一头雾水,后来经过开发同 ...
- Linux驱动:SPI驱动编写要点
题外话:面对成功和失败,一个人有没有“冠军之心”,直接影响他的表现. 几周前剖析了Linux SPI 驱动框架,算是明白个所以然,对于这么一个庞大的框架,并不是每一行代码都要自己去敲,因为前人已经把这 ...
- start with connect by prior
start with connect by prior的使用: select … from tablename start with 条件1 connect by 条件2 where 条件3; sta ...
- gitHub-高仿58同城加载动画
导入方式: /build.gradle repositories { maven { url "https://jitpack.io" } } /app/build.gradle ...
- 关于类型Type
每一个JC语法节点都含有type属性,因为做为所有JC语法节点的父节点JCTree含有type属性.其继承关系如下图. 下面看一下Type类的定义及重要的属性. public class Type i ...
- Go 开发
0.参数传递永远是值传递,地址也是一种值 1.go 开发环境的配置 2.import 包的几种形式: 1)_,默认导入一个包时,会将包中内容导入再执行包中的init()方法,有时并不需要某个包,只是想 ...
- centos7-安装mysql5.6.36
本地安装了mysql5.7, 但和springboot整合jpa时会出现 hibernateException, 不知道为什么, 换个mysql5.6版本的mysql, 源码安装, cmake一直过 ...