中间件的作用
灵活应用中间件机制,可以有效扩展架构的功能。中间件主要的作用有:拦截
、重整
- 拦截:比如通过中间件判断用户权限,如果没有权限则中止后续执行
- 重整:比如通过中间件对前端发来的数据进行验证,并转化为期望的类型
拦截
在执行API路由
之前,对请求参数进行判断,如果不符合预期,就拒绝
比如中间件test
,判断当前运行环境,如果不是测试环境
就抛出异常:
src/module-system/a-base-sync/backend/src/bean/middleware.test.js
- 1module.exports = ctx => {
- 2 class Middleware {
- 3 async execute(options, next) {
- 4 if (!ctx.app.meta.isTest) ctx.throw(403);
- 5 // next
- 6 await next();
- 7 }
- 8 }
- 9 return Middleware;
- 10};
重整
在执行API路由
之前,对请求参数进行调整,使其符合预期的格式(如果不符合预期也可以拒绝)
比如中间件validate
,验证前端发送的表单数据,并转换为预期的数据类型:
src/module-system/a-validation/backend/src/bean/middleware.validate.js
- 1module.exports = ctx => {
- 2 const moduleInfo = ctx.app.meta.mockUtil.parseInfoFromPackage(__dirname);
- 3 class Middleware {
- 4 async execute(options, next) {
- 5 // must exists
- 6 const validator = options.validator;
- 7 if (!validator) ctx.throw.module(moduleInfo.relativeName, 1001);
- 8 // params
- 9 const module = options.module || ctx.module.info.relativeName;
- 10 const schema = options.schema || (ctx.meta._validator && ctx.meta._validator.schema);
- 11 const data = ctx.request.body[options.data || 'data'];
- 12 // if error throw 422
- 13 await ctx.bean.validation.validate({
- 14 module,
- 15 validator,
- 16 schema,
- 17 data,
- 18 });
- 19 // next
- 20 await next();
- 21 }
- 22 }
- 23 return Middleware;
- 24};
中间件的类型
EggBornJS主要有三个来源的中间件:
EggJS中间件
:在EggBornJS中可以直接使用EggJS生态的中间件,请参考:Egg中间件EggBornJS系统中间件
:EggBornJS提供的系统级别的中间件,比如:instance
EggBornJS模块中间件
:EggBornJS通过业务模块提供的中间件,比如:transaction
、auth
、right
、validate
模块中间件的开发
在这里,我们通过一个虚拟的需求,用中间件实现拦截
和重整
的逻辑:
- 前端发送两个参数:a、b,后端计算二者之和,并返回前端
- 拦截:中间件判断参数是否为空,如果为空则中止后续执行
- 重整:将参数类型强制转换为Integer类型
- 声明中间件
src/suite-vendor/test-party/modules/test-party/backend/src/config/config.js
- 1// middlewares
- 2config.middlewares = {
- 3 testInterception: {
- 4 bean: 'testInterception',
- 5 global: false,
- 6 dependencies: 'instance',
- 7 },
- 8 testRestructuring: {
- 9 bean: 'testRestructuring',
- 10 global: false,
- 11 dependencies: 'instance',
- 12 },
- 13};
名称 | 说明 |
---|---|
bean | 用于实现中间件逻辑的Bean组件名称 |
global | 是否为全局中间件 ,全局中间件会自动加载,局部中间件 需要手动指定 |
dependencies | 标明此中间件依赖哪些中间件,从而在那些中间件后面加载。一般要依赖于instance ,因为instance 提供了多实例 的基础逻辑 |
- 中间件:定义Bean组件
src/suite-vendor/test-party/modules/test-party/backend/src/bean/middleware.interception.js
- 1module.exports = ctx => {
- 2 class Middleware {
- 3 async execute(options, next) {
- 4 const { a, b } = ctx.request.body;
- 5 if (a === undefined || b === undefined) return ctx.throw(1002); // 1002: 'Incomplete Parameters'
- 6 // next
- 7 await next();
- 8 }
- 9 }
- 10 return Middleware;
- 11};
src/suite-vendor/test-party/modules/test-party/backend/src/bean/middleware.restructuring.js
- 1module.exports = ctx => {
- 2 class Middleware {
- 3 async execute(options, next) {
- 4 const { a, b } = ctx.request.body;
- 5 ctx.request.body.a = parseInt(a);
- 6 ctx.request.body.b = parseInt(b);
- 7 // next
- 8 await next();
- 9 }
- 10 }
- 11 return Middleware;
- 12};
- 中间件:注册Bean组件
src/suite-vendor/test-party/modules/test-party/backend/src/beans.js
- 1const middlewareTestInterception = require('./bean/middleware.interception.js');
- 2const middlewareTestRestructuring = require('./bean/middleware.restructuring.js');
- 3
- 4module.exports = app => {
- 5 const beans = {
- 6 // middleware
- 7 'middleware.testInterception': {
- 8 mode: 'ctx',
- 9 bean: middlewareTestInterception,
- 10 },
- 11 'middleware.testRestructuring': {
- 12 mode: 'ctx',
- 13 bean: middlewareTestRestructuring,
- 14 },
- 15 };
- 16 return beans;
- 17};
注册名称 | 场景 | 所属模块 | global | beanFullName |
---|---|---|---|---|
testInterception | middleware | test-party | false | test-party.middleware.testInterception |
testRestructuring | middleware | test-party | false | test-party.middleware.testRestructuring |
- 使用中间件
因为testInterception
和testRestructuring
是局部中间件,因此需要手动在API路由上指定
src/suite-vendor/test-party/modules/test-party/backend/src/routes.js
- 1// test/feat/middleware
- 2{ method: 'post', path: 'test/feat/middleware/interception', controller: 'testFeatMiddleware', middlewares: 'test,testInterception' },
- 3{ method: 'post', path: 'test/feat/middleware/restructuring', controller: 'testFeatMiddleware', middlewares: 'test,testInterception,testRestructuring' },
- 后端控制器
src/suite-vendor/test-party/modules/test-party/backend/src/controller/test/feat/middleware.js
- 1module.exports = app => {
- 2
- 3 class TestController extends app.Controller {
- 4
- 5 async interception() {
- 6 const { a, b } = this.ctx.request.body;
- 7 const c = parseInt(a) + parseInt(b);
- 8 this.ctx.success(c);
- 9 }
- 10
- 11 async restructuring() {
- 12 const { a, b } = this.ctx.request.body;
- 13 const c = a + b;
- 14 this.ctx.success(c);
- 15 }
- 16
- 17 }
- 18
- 19 return TestController;
- 20};
中间件的使用
- 全局中间件
全局中间件自动加载,不需在后端路由中指定
- 局部中间件
只需设置后端API路由
参数middlewares
,如:
- 1{ method: 'post', path: 'version/update', controller: version, middlewares: 'inner' }
- 中间件参数
可通过后端API路由
参数meta
指定中间件的参数,如:
src/suite-vendor/test-party/modules/test-party/backend/src/routes.js
- 1const testKitchensinkGuide = require('./controller/kitchen-sink/guide.js');
- 2
- 3module.exports = app => {
- 4 const routes = [
- 5 {
- 6 method: 'post',
- 7 path: 'kitchen-sink/guide/echo9',
- 8 controller: 'testKitchensinkGuide',
- 9 action: 'echo9',
- 10 middlewares: 'test,transaction'
- 11 meta: {
- 12 right: {
- 13 type: 'resource',
- 14 name: 'kitchenSink',
- 15 },
- 16 },
- 17 },
- 18 ];
- 19 return routes;
- 20};
名称 | 说明 |
---|---|
meta.right | 全局中间件right 的参数,通过配置type和name,从而验证当前用户是否具有访问此API的权限 |
- 禁用中间件
可通过两种方式禁用中间件:
1. config参数配置
通过属性ignore
指定哪些API路由
禁用此中间件
a-instance/backend/src/config/config.js
- 1// middlewares
- 2config.middlewares = {
- 3 instance: {
- 4 global: true,
- 5 dependencies: 'cachemem',
- 6 ignore: /(\/version\/(update))/,
- 7 },
- 8};
2. 后端API路由
直接在后端API路由
中通过属性enable
禁用中间件
a-base/backend/src/routes.js
- 1{ method: 'post', path: 'auth/echo', controller: auth,
- 2 meta: {
- 3 auth: {
- 4 enable: false
- 5 }
- 6 }
- 7}
名称 | 说明 |
---|---|
auth.enable | 禁用全局中间件auth |
当某中间件被禁用后,依赖于此中间件的其他中间件也自动被禁用
评论: