• Fastify 系列教程二 (中间件、钩子函数和装饰器)


    Fastify 系列教程:

    中间件

    Fastify 提供了与 ExpressRestify 中间件兼容的异步中间件引擎

    所以,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 的 reqres,而路由中的 requestreply 是经过 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)

    注意 reqresrequestreply 的区别。

    参数 描述
    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 查看我的个人动态。

  • 相关阅读:
    PowerTalk第一个版本儿控件
    PowerTalk第二个版本,支持(Msn回复信息)
    自然语言处理著作或期刊名称
    自然语言处理(NLP)网上资源整理 (转)
    TFIDF
    计算机科学及工程
    自然语言处理与计算语言学书籍汇总之一:国外书籍
    UVa 10696 f91
    缓存
    操作必须使用一个可更新的查询(转)
  • 原文地址:https://www.cnblogs.com/smartXiang/p/7736117.html
Copyright © 2020-2023  润新知