php yield 中间件,Middleware / 中间件

本文详细介绍了Koa框架中的中间件概念,包括其格式、配置管理、使用app.use与扩展、异步配置以及常见问题。通过实例演示如何创建并配置打印执行时间的中间件,并讨论了中间件配置顺序、生效判断和数据传递的方法。
摘要由CSDN通过智能技术生成

Middleware / 中间件

Middleware 称之为中间件,是 Koa 中一个非常重要的概念,利用中间件,可以很方便的处理用户的请求。由于 ThinkJS 3.0 是基于 Koa@2 版本之上构建的,所以完全兼容 Koa 里的中间件。

中间件格式

module.exports = options => {

return (ctx, next) => {

// do something

}

}

中间件格式为一个高阶函数,外部的函数接收一个 options 参数,这样方便中间件提供一些配置信息,用来开启/关闭一些功能。执行后返回另一个函数,这个函数接收 ctx, next 参数,其中 ctx 为 context 的简写,是当前请求生命周期的一个对象,存储了当前请求的一些相关信息,next 为调用后续的中间件,返回值是 Promise,这样可以很方便的处理后置逻辑。

整个中间件执行过程是个洋葱模型,类似下面这张图:

6da2d70c4b21464bb54e5d89eaaf30dc.png

假如要实现一个打印当前请求执行时间的 middleware,可以用类似下面的方式:

const defaultOptions = {

consoleExecTime: true // 是否打印执行时间的配置

}

module.exports = (options = {}) => {

// 合并传递进来的配置

options = Object.assign({}, defaultOptions, options);

return (ctx, next) => {

if(!options.consoleExecTime) {

return next(); // 如果不需要打印执行时间,直接调用后续执行逻辑

}

const startTime = Date.now();

let err = null;

// 调用 next 统计后续执行逻辑的所有时间

return next().catch(e => {

err = e; // 这里先将错误保存在一个错误对象上,方便统计出错情况下的执行时间

}).then(() => {

const endTime = Date.now();

console.log(`request exec time: ${endTime - startTime}ms`);

if(err) return Promise.reject(err); // 如果后续执行逻辑有错误,则将错误返回

})

}

}

在 Koa 中,可以通过调用 app.use 的方式来使用中间件,如:

const app = new Koa();

const execTime = require('koa-execTime'); // 引入统计执行时间的模块

app.use(execTime({})); // 需要将这个中间件第一个注册,如果还有其他中间件放在后面注册

通过 app.use 的方式使用中间件,不利于中间件的统一维护。

扩展 app 参数

默认的中间件外层一般只是传递了 options 参数,有的中间件需要读取 app 相关的信息,框架在这块做了扩展,自动将 app 对象传递到中间件中。

module.exports = (options, app) => {

// 这里的 app 为 think.app 对象

return (ctx, next) => {

}

}

如果在中间件中需要用到 think 对象上的一些属性或者方法,那么可以通过 app.think.xxx 来获取。

配置格式

为了方便管理和使用中间件,框架使用统一的配置文件来管理中间件,配置文件为 src/config/middleware.js(多模块项目配置文件为 sr/common/config/middleware.js)。

const path = require('path')

const isDev = think.env === 'development'

module.exports = [

{

handle: 'meta', // 中间件处理函数

options: { // 当前中间件需要的配置

logRequest: isDev,

sendResponseTime: isDev,

},

},

{

handle: 'resource',

enable: isDev, // 是否开启当前中间件

options: {

root: path.join(think.ROOT_PATH, 'www'),

publicPath: /^\/(static|favicon\.ico)/,

},

}

]

配置项为项目中要使用的中间件列表,每一项支持 handle,enable,options,match 等属性。

handle

中间件的处理函数,可以用系统内置的,也可以是引入外部的,也可以是项目里的中间件。

handle 的函数格式为:

module.exports = (options, app) => {

return (ctx, next) => {

}

}

这里中间件接收的参数除了 options 外,还多了个 app 对象,该对象为 Koa Application 的实例。

enable

是否开启当前的中间件,比如:某个中间件只在开发环境下才生效。

{

handle: 'resouce',

enable: think.env === 'development' //这个中间件只在开发环境下生效

}

options

传递给中间件的配置项,格式为一个对象,中间件里获取到这个配置。

module.exports = [

{

options: {

key: value

}

}

]

有时候需要的配置项需要从远程获取,如:配置值保存在数据库中,这时候就要异步从数据库中获取,这时候可以将 options 定义为一个函数来完成:

module.exports = [

{

// 将 options 定义为一个异步函数,将获取到的配置返回

options: async () => {

const config = await getConfigFromDb();

return {

key: config.key,

value: config.value

}

}

}

]

match

匹配特定的规则后才执行该中间件,支持二种方式,一种是路径匹配,一种是自定义函数匹配。如:

module.exports = [

{

handle: 'xxx-middleware',

match: '/resource' //请求的 URL 是 /resource 时才生效这个 middleware,全匹配

}

]

module.exports = [

{

handle: 'xxx-middleware',

match: /^\/resource/ //请求的 URL 命中这个正则时才生效

}

]

module.exports = [

{

handle: 'xxx-middleware',

match: ctx => { // match 为一个函数,将 ctx 传递给这个函数,如果返回结果为 true,则启用该 middleware

return true;

}

}

]

框架内置的中间件

框架内置了几个中间件,可以通过字符串的方式直接引用。

module.exports = [

{

handle: 'meta', // 内置的中间件不用手工 require 进来,直接通过字符串的方式引用

options: {}

}

]

meta 显示一些 meta 信息,如:发送 ThinkJS 的版本号,接口的处理时间等等

resource 处理静态资源,生产环境建议关闭,直接用 webserver 处理即可。

trace 处理报错,开发环境将详细的报错信息显示处理,也可以自定义显示错误页面。

payload 处理表单提交和文件上传,类似于 koa-bodyparser 等 middleware

router 路由解析,包含自定义路由解析

logic logic 调用,数据校验

controller controller 和 action 调用

项目中自定义的中间件

有时候项目中根据一些特定需要添加中间件,那么可以放在 src/middleware 目录下,然后就可以直接通过字符串的方式引用了。

如:添加了 src/middleware/csrf.js,那么就可以直接通过 csrf 字符串引用这个中间件。

module.exports = [

{

handle: 'csrf',

options: {}

}

]

引入外部的中间件

引入外部的中间件非常简单,只需要 require 进来即可。

const csrf = require('csrf');

module.exports = [

...,

{

handle: csrf,

options: {}

},

...

]

常见问题

中间件配置是否需要考虑顺序?

中间件执行是按照配置的排列顺序执行的,所以需要开发者考虑配置的顺序。

怎么看当前环境下哪些中间件生效?

可以通过 DEBUG=koa:application node development.js 来启动项目,这样控制台下会看到 koa:application use ... 相关的信息。

注意:如果启动了多个 worker,那么会打印多遍。

怎么透传数据到 Logic、Controller 中?

有时候需要在中间件里设置一些数据,然后在后续的 Logic、Controller 中获取,此时可以通过 ctx.state 完成,具体请见 透传数据。

怎么设置数据到 GET/POST 数据中?

在中间件里可以通过 ctx.param、ctx.post 等方法来获取 query 参数或者表单提交上来的数据,但有些中间件里希望设置一些参数值、表单值以便在后续的 Logic、Controller 中获取,这时候可以通过 ctx.param、ctx.post 设置:

// 设置参数 name=value,后续在 Logic、Controller 中可以通过 this.get('name') 获取该值

// 如果原本已经有该参数,那么会覆盖

ctx.param('name', 'value');

// 设置 post 值,后续 Logic、Controller 中可以通过 this.post('name2') 获取该值

ctx.post('name2', 'value');

中间件的配置是否可以放在 config.js 中?

不合适,中间件提供了 options 参数用来设置配置,不需要把额外的参数配置放在 config.js 中。

module.exports = [

{

handle: xxxMiddleware,

options: { // 传递给中间件的配置

key1: value1,

key2: think.env === 'development' ? value2 : value3

}

}

]

如果有些配置跟 env 相关,那么可以在此进行判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值