Benifits of Middleware
Flexible use of middleware
mechanism can effectively extend the functions of the architecture. The main functions of middleware are interception
, reorganization
, injection
interception
:For example, judging user privileges through middleware and suspending subsequent execution if no privileges existreorganization
:For example, the data sent from the frontend is validated by middleware and transformed into the desired typeinjection
:For example, injecting objects intoctx
through middleware to provide the necessary basic functionality for subsequent code
Interception
Before executing the API Route
, judge the request parameters. If they do not meet the expectations, reject this request
For example, middleware test
determines the current running environment. If it is not a test environment
, an exception will be thrown:
egg-born-backend/app/middleware/test.js
module.exports = () => {
return async function test(ctx, next) {
if (!ctx.app.meta.isTest) ctx.throw(403);
// next
await next();
};
};
Reorganization
Before executing the API Route
, adjust the request parameters to make them conform to the expected data type
For example, middleware validate
verifies the form data sent by fronend and converts it to the expected data type
a-validation/backend/src/config/middleware/validate.js
module.exports = (options2, app) => {
const moduleInfo = app.meta.mockUtil.parseInfoFromPackage(__dirname);
return async function validate(ctx, next, options) {
// must exists
const validator = options.validator;
if (!validator) ctx.throw.module(moduleInfo.relativeName, 1001);
// params
const module = options.module || ctx.module.info.relativeName;
const schema = options.schema || (ctx.meta._validator && ctx.meta._validator.schema);
const data = ctx.request.body[options.data || 'data'];
// if error throw 422
await ctx.meta.validation.validate({
module,
validator,
schema,
data,
});
// next
await next();
};
};
Injection
Before executing API Route
, inject some object instances into the object of ctx
For example, middleware cachemem
injects memory based cache object into ctx
:
a-cache/backend/src/config/middleware/cachemem.js
const memFn = require('./adapter/mem.js');
const CACHE = Symbol('CTX#__CACHE');
module.exports = () => {
return async function cachemem(ctx, next) {
ctx.cache = ctx.cache || {};
Object.defineProperty(ctx.cache, 'mem', {
get() {
if (ctx.cache[CACHE] === undefined) {
ctx.cache[CACHE] = new (memFn(ctx))();
}
return ctx.cache[CACHE];
},
});
// next
await next();
};
};
Types of Middleware
EggBornJS has three main sources of middleware:
- EggJS Middleware
- EggBornJS System Middleware
- EggBornJS Module Middleware
EggJS Middleware
The middlewares of EggJS ecosystem can be used directly in EggBornJS
See also: EggJS Middleware
EggBornJS System Middleware
To meet the application requirements of common scenarios, EggBornJS has three built-in system middleware: inner
、test
、transaction
1. inner
The API Route
can perform other API Route
through ctx.performAction
, which is called inner access
Specify the middleware inner
in the API Route
to restrict the route only to be inner access
{ method: 'post', path: 'version/update', controller: version, middlewares: 'inner' }
Middleware
inner
can also be regarded as one of the API authorization policies
2. test
Specify the middleware test
in the API Route
to restrict the route only to be performed in the test environment
{ method: 'post', path: 'version/test', controller: version, middlewares: 'test' }
Middleware
test
can also be regarded as one of the API authorization policies
3. transaction
Specify the middleware transaction
in the API Route
to enable database transaction
for this route
{ method: 'post', path: 'test/echo', controller: test, middlewares: 'transaction'}
EggBornJS Module Middleware
EggBornJS allows middleware to be developed in modules, which can be used by the same module or the other modules
Development of Module Middleware
Here, through a virtual requirement, we use middleware to achieve the three functions: interception',
reorganization’ and `injection’:
- Following the previous code, the frontend sends two parameters to the backend:
message
,markCount
- interception:The middleware determines whether
message
is empty, and if it is empty, terminates subsequent execution - reorganization:Mandatory conversion of
markCount
toInteger
type - injection:Inject a method to be called in subsequent code
Declaration of Middleware
src/suite-vendor/test-party/modules/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/suite-vendor/test-party/modules/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/suite-vendor/test-party/modules/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/suite-vendor/test-party/modules/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/suite-vendor/test-party/modules/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 testInterception
、testRestructuring
and testInjection
are all local middlewares, you need to specify it manually on the API route
src/suite-vendor/test-party/modules/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/suite-vendor/test-party/modules/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;
};
Usage of Middleware
Global Middleware
The global middleware is loaded automatically and does not need to be specified in the API route
Local Middleware
The local middleware need to be explicitly specified in the API route
{ method: 'post', path: 'version/update', controller: version, middlewares: 'inner' }
Middleware Parameters
You can specify the middleware parameters through the meta
of the API route
src/suite-vendor/test-party/modules/test-party/backend/src/routes.js
const testKitchensinkGuide = require('./controller/kitchen-sink/guide.js');
module.exports = app => {
const routes = [
{
method: 'post',
path: 'kitchen-sink/guide/echo9',
controller: testKitchensinkGuide,
action: 'echo9',
middlewares: 'test,transaction'
meta: {
right: {
type: 'function',
name: 'kitchenSink',
},
},
},
];
return routes;
};
Name | Description |
---|---|
meta.right | parameters of the global middleware right , which be used to verify whether the current user has access to the API route by the specified type and name |
Disable Middleware
Middleware can be disabled in two ways:
1. config
Disable middleware for specified API Routes
by the attribute ignore
of config
a-instance/backend/src/config/config.js
// middlewares
config.middlewares = {
instance: {
global: true,
dependencies: 'cachemem',
ignore: /(\/version\/(update))/,
},
};
2. API Route
Disable middleware directly in the API route through the attribute enable
a-base/backend/src/routes.js
{ method: 'post', path: 'auth/echo', controller: auth,
meta: {
auth: {
enable: false
}
}
}
Name | Description |
---|---|
auth.enable | Disable the global middleware auth |
When a middleware is disabled, other middlewares dependent on the middleware are also automatically disabled
Comments: