Flexible use of middleware mechanism can effectively extend the functions of the architecture. The main functions of middleware are interception, reorganization, injection

  1. interception:For example, judging user privileges through middleware and suspending subsequent execution if no privileges exist
  2. reorganization:For example, the data sent from the frontend is validated by middleware and transformed into the desired type
  3. injection:For example, injecting objects into ctx through middleware to provide the necessary basic functionality for subsequent code

Here, through a virtual requirement, we use middleware to achieve the three functions: interception',reorganization’ and `injection’:

  1. Following the previous code, the frontend sends two parameters to the backend: message, markCount
  2. interception:The middleware determines whether message is empty, and if it is empty, terminates subsequent execution
  3. reorganization:Mandatory conversion of markCount to Integer type
  4. injection:Inject a method to be called in subsequent code

Declaration of Middleware

src/module/test-party/backend/src/config/config.js

// middlewares
config.middlewares = {
  testInterception: {
    global: false,
    dependencies: 'instance',
  },
  testRestructuring: {
    global: false,
    dependencies: 'instance',
  },
  testInjection: {
    global: false,
    dependencies: 'instance',
  },
};
Name Description
global Whether global middleware or not, the global middleware will be loaded automatically, and the local middleware needs to be specified on the API route manually
dependencies Indicate which middlewares this middleware relies on to load after those middlewares. Generally, it depends on middleware instance, because middleware instance provides the basic logic of multi-instance

Definition of Middleware

src/module/test-party/backend/src/config/middleware/interception.js

module.exports = () => {
  return async function interception(ctx, next) {
    const { a, b } = ctx.request.body;
    if (a === undefined || b === undefined) return ctx.throw(1002); // 1002: 'Incomplete Parameters'
    // next
    await next();
  };
};

src/module/test-party/backend/src/config/middleware/restructuring.js

module.exports = () => {
  return async function restructuring(ctx, next) {
    const { a, b } = ctx.request.body;
    ctx.request.body.a = parseInt(a);
    ctx.request.body.b = parseInt(b);
    // next
    await next();
  };
};

src/module/test-party/backend/src/config/middleware/injection.js

module.exports = () => {
  return async function injection(ctx, next) {
    ctx.meta.__plus = (a, b) => {
      return a + b;
    };
    // next
    await next();
  };
};

Reference Middleware

src/module/test-party/backend/src/config/middlewares.js

const interception = require('./middleware/interception.js');
const restructuring = require('./middleware/restructuring.js');
const injection = require('./middleware/injection.js');

module.exports = {
  testInterception: interception,
  testRestructuring: restructuring,
  testInjection: injection,
};

Usage of Middleware

Because testInterceptiontestRestructuring and testInjection are all local middlewares, you need to specify it manually on the API route

src/module/test-party/backend/src/routes.js

// test/feat/middleware
{ method: 'post', path: 'test/feat/middleware/interception', controller: testFeatMiddleware, middlewares: 'test,testInterception' },
{ method: 'post', path: 'test/feat/middleware/restructuring', controller: testFeatMiddleware, middlewares: 'test,testInterception,testRestructuring' },
{ method: 'post', path: 'test/feat/middleware/injection', controller: testFeatMiddleware, middlewares: 'test,testInterception,testRestructuring,testInjection' },

Backend Logic

src/module/test-party/backend/src/controller/test/feat/middleware.js

module.exports = app => {

  class TestController extends app.Controller {

    async interception() {
      const { a, b } = this.ctx.request.body;
      const c = parseInt(a) + parseInt(b);
      this.ctx.success(c);
    }

    async restructuring() {
      const { a, b } = this.ctx.request.body;
      const c = a + b;
      this.ctx.success(c);
    }

    async injection() {
      const { a, b } = this.ctx.request.body;
      const c = this.ctx.meta.__plus(a, b);
      this.ctx.success(c);
    }

  }

  return TestController;
};

Unit-Test

Here, we use unit-tests to verify that the middleware mechanism works as expected

src/module/test-party/backend/test/controller/test/feat/middleware.test.js

const { app, mockUrl, mockInfo, assert } = require('egg-born-mock')(__dirname);

describe('test/controller/test/feat/middleware.test.js', () => {

  it('action:interception', async () => {
    // success
    let result = await app.httpRequest()
      .post(mockUrl('test/feat/middleware/interception'))
      .send({
        a: '1',
        b: '2',
      });
    assert.equal(result.body.data, 3);

    // fail
    result = await app.httpRequest()
      .post(mockUrl('test/feat/middleware/interception'))
      .send();
    assert.equal(result.status, 500);
  });

  it('action:restructuring', async () => {
    // success
    const result = await app.httpRequest()
      .post(mockUrl('test/feat/middleware/restructuring'))
      .send({
        a: '1',
        b: '2',
      });
    assert.equal(result.body.data, 3);
  });

  it('action:injection', async () => {
    // success
    const result = await app.httpRequest()
      .post(mockUrl('test/feat/middleware/injection'))
      .send({
        a: '1',
        b: '2',
      });
    assert.equal(result.body.data, 3);
  });

});