灵活应用中间件机制,可以有效扩展架构的功能。中间件主要的作用有:拦截重整

  1. 拦截:比如通过中间件判断用户权限,如果没有权限则中止后续执行
  2. 重整:比如通过中间件对前端发来的数据进行验证,并转化为期望的类型

在这里,我们通过一个虚拟的需求,用中间件实现拦截重整的逻辑:

  1. 需求:前端发送两个参数:a、b,后端计算二者之和,并返回前端
  2. 拦截:中间件判断参数是否为空,如果为空则中止后续执行
  3. 重整:将参数类型强制转换为Integer类型

创建中间件

1. 声明中间件

src/suite-vendor/test-party/modules/test-party/backend/src/config/config.js

  1. 1// middlewares
  2. 2config.middlewares = {
  3. 3 testInterception: {
  4. 4 bean: 'testInterception',
  5. 5 global: false,
  6. 6 dependencies: 'instance',
  7. 7 },
  8. 8 testRestructuring: {
  9. 9 bean: 'testRestructuring',
  10. 10 global: false,
  11. 11 dependencies: 'instance',
  12. 12 },
  13. 13};
名称 说明
bean 用于实现中间件逻辑的Bean组件名称
global 是否为全局中间件,全局中间件会自动加载,局部中间件需要手动指定
dependencies 标明此中间件依赖哪些中间件,从而在那些中间件后面加载。一般要依赖于instance,因为instance提供了多实例的基础逻辑

2. 定义中间件Bean组件

src/suite-vendor/test-party/modules/test-party/backend/src/bean/middleware.interception.js

  1. 1module.exports = ctx => {
  2. 2 class Middleware {
  3. 3 async execute(options, next) {
  4. 4 const { a, b } = ctx.request.body;
  5. 5 if (a === undefined || b === undefined) return ctx.throw(1002); // 1002: 'Incomplete Parameters'
  6. 6 // next
  7. 7 await next();
  8. 8 }
  9. 9 }
  10. 10 return Middleware;
  11. 11};

src/suite-vendor/test-party/modules/test-party/backend/src/bean/middleware.restructuring.js

  1. 1module.exports = ctx => {
  2. 2 class Middleware {
  3. 3 async execute(options, next) {
  4. 4 const { a, b } = ctx.request.body;
  5. 5 ctx.request.body.a = parseInt(a);
  6. 6 ctx.request.body.b = parseInt(b);
  7. 7 // next
  8. 8 await next();
  9. 9 }
  10. 10 }
  11. 11 return Middleware;
  12. 12};

3. 注册中间件Bean组件

src/suite-vendor/test-party/modules/test-party/backend/src/beans.js

  1. 1const middlewareTestInterception = require('./bean/middleware.interception.js');
  2. 2const middlewareTestRestructuring = require('./bean/middleware.restructuring.js');
  3. 3
  4. 4module.exports = app => {
  5. 5 const beans = {
  6. 6 // middleware
  7. 7 'middleware.testInterception': {
  8. 8 mode: 'ctx',
  9. 9 bean: middlewareTestInterception,
  10. 10 },
  11. 11 'middleware.testRestructuring': {
  12. 12 mode: 'ctx',
  13. 13 bean: middlewareTestRestructuring,
  14. 14 },
  15. 15 };
  16. 16 return beans;
  17. 17};

使用中间件

1. 配置API路由

因为testInterceptiontestRestructuring是局部中间件,因此需要手动在API路由上指定

src/suite-vendor/test-party/modules/test-party/backend/src/routes.js

  1. 1// test/feat/middleware
  2. 2{ method: 'post', path: 'test/feat/middleware/interception', controller: 'testFeatMiddleware', middlewares: 'test,testInterception' },
  3. 3{ method: 'post', path: 'test/feat/middleware/restructuring', controller: 'testFeatMiddleware', middlewares: 'test,testInterception,testRestructuring' },

2. 后端控制器逻辑

src/suite-vendor/test-party/modules/test-party/backend/src/controller/test/feat/middleware.js

  1. 1module.exports = app => {
  2. 2
  3. 3 class TestController extends app.Controller {
  4. 4
  5. 5 async interception() {
  6. 6 const { a, b } = this.ctx.request.body;
  7. 7 const c = parseInt(a) + parseInt(b);
  8. 8 this.ctx.success(c);
  9. 9 }
  10. 10
  11. 11 async restructuring() {
  12. 12 const { a, b } = this.ctx.request.body;
  13. 13 const c = a + b;
  14. 14 this.ctx.success(c);
  15. 15 }
  16. 16
  17. 17 }
  18. 18
  19. 19 return TestController;
  20. 20};

src/suite-vendor/test-party/modules/test-party/backend/src/controllers.js

  1. 1const testFeatMiddleware = require('./controller/test/feat/middleware.js');
  2. 2
  3. 3module.exports = app => {
  4. 4 const controllers = {
  5. 5 testFeatMiddleware,
  6. 6 };
  7. 7 return controllers;
  8. 8};

单元测试

在这里,我们采用单元测试来验证中间件机制是否按预期运作

src/suite-vendor/test-party/modules/test-party/backend/test/controller/test/feat/middleware.test.js

  1. 1const { app, mockUrl, mockInfo, assert } = require('egg-born-mock')(__dirname);
  2. 2
  3. 3describe('test/controller/test/feat/middleware.test.js', () => {
  4. 4
  5. 5 it('action:interception', async () => {
  6. 6 // success
  7. 7 let result = await app.httpRequest()
  8. 8 .post(mockUrl('test/feat/middleware/interception'))
  9. 9 .send({
  10. 10 a: '1',
  11. 11 b: '2',
  12. 12 });
  13. 13 assert.equal(result.body.data, 3);
  14. 14
  15. 15 // fail
  16. 16 result = await app.httpRequest()
  17. 17 .post(mockUrl('test/feat/middleware/interception'))
  18. 18 .send();
  19. 19 assert.equal(result.status, 500);
  20. 20 });
  21. 21
  22. 22 it('action:restructuring', async () => {
  23. 23 // success
  24. 24 const result = await app.httpRequest()
  25. 25 .post(mockUrl('test/feat/middleware/restructuring'))
  26. 26 .send({
  27. 27 a: '1',
  28. 28 b: '2',
  29. 29 });
  30. 30 assert.equal(result.body.data, 3);
  31. 31 });
  32. 32
  33. 33});