├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib └── index.js ├── package.json └── test └── index.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | # Matches the exact files either package.json or .travis.yml 15 | [{package.json,.travis.yml}] 16 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/coverage/*.js 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "standard", 3 | "rules": { 4 | "indent": ["error", 4], 5 | "space-before-function-paren": ["error", { 6 | "anonymous": "never", 7 | "named": "always", 8 | "asyncArrow": "always" 9 | }], 10 | "semi": ["error", "always"] 11 | }, 12 | "env": { 13 | "node": true, 14 | "mocha": true 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Coverage directory used by tools like istanbul 15 | test/coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Dependency directories 21 | node_modules/ 22 | 23 | package-lock.json 24 | 25 | .nyc_output 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lan Bo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # express-dynamic-middleware 2 | To add, remove dynamic middleware in runtime for express. 3 | 4 | More detail information in [here](https://github.com/lanbomo/express-dynamic-middleware). 5 | 6 | ## Install 7 | 8 | ```bash 9 | npm install express-dynamic-middleware 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```js 15 | const express = require('express'); 16 | 17 | // import express-dynamic-middleware 18 | const dynamicMiddleware = require('express-dynamic-middleware'); 19 | 20 | 21 | // create auth middleware 22 | const auth = function(req, res, next) { 23 | if (req.get('Authorization') === 'Basic') { 24 | next(); 25 | } else { 26 | res.status(401).end('Unauthorization'); 27 | } 28 | }; 29 | 30 | // create dynamic middleware 31 | const dynamic = dynamicMiddleware.create(auth); 32 | 33 | // create express app 34 | const app = express(); 35 | 36 | // use the dynamic middleware 37 | app.use(dynamic.handle()); 38 | 39 | // unuse auth middleware 40 | dynamic.unuse(auth); 41 | ``` 42 | 43 | ## API 44 | 45 | ### dynamicMiddleware.create([middlewares]) 46 | 47 | 48 | ```js 49 | const dynamic = dynamicMiddleware.create(middlewares); 50 | ``` 51 | 52 | 53 | Use `create` to create dynamic middleware `dynamic`, the argument `middlewares` cloud be a middleware function like `function(req, res, next){}` or a middleware functions array like `[function(req, res, next){}, function(req, res, next){}`; 54 | 55 | ### dynamic 56 | 57 | `dynamic` is created by `dynamicMiddleware.create`, it has some functions to manage the middlewares. 58 | 59 | #### use 60 | 61 | ```js 62 | dynamic.use(function(req, res, next) { 63 | // do something 64 | }); 65 | ``` 66 | 67 | To call `use` function to add middleware in runtime. 68 | 69 | #### unuse 70 | 71 | ```js 72 | const auth = function(req, res, next) { 73 | if (req.get('Authorization') === 'Basic') { 74 | next(); 75 | } else { 76 | res.status(401).end('Unauthorization'); 77 | } 78 | }; 79 | 80 | // use auth middleware 81 | dynamic.use(auth); 82 | 83 | // remove the auth middleware 84 | dynamic.unuse(auth); 85 | ``` 86 | 87 | To call `unuse` function to remove the middleware. 88 | 89 | #### clean 90 | 91 | ```js 92 | const dynamic = dynamicMiddleware.create([function(){}, function(){}]); 93 | 94 | // clean the dynamic middlewares 95 | dynamic.clean(); 96 | ``` 97 | 98 | To call `clean` function to remove all the middlewares 99 | 100 | #### handle 101 | 102 | ```js 103 | const app = express(); 104 | 105 | // return the dynamic middlewares to the express app 106 | app.use(dynamic.handle()); 107 | ``` 108 | 109 | To call `handle` function to get the dynamic middleware function to the express app 110 | 111 | #### get 112 | 113 | ```js 114 | const dynamic = dynamicMiddleware.create([function(){}, function(){}]); 115 | 116 | console.log(dynamic.get()); // [function(){}, function(){}] 117 | console.log(dynamic.get().length); // 2 118 | 119 | dynamic.use(function() {}); 120 | 121 | console.log(dynamic.get().length); // 3 122 | 123 | dynamic.clean(); 124 | 125 | console.log(dynamic.get()); // [] 126 | console.log(dynamic.get().length); // 0 127 | ``` 128 | 129 | To call `get` function to get the dynamic middleware stack. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * express dynamic middleware 3 | */ 4 | 5 | module.exports = require('./lib'); 6 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * express dynamic middleware 3 | */ 4 | 5 | const async = require('async'); 6 | 7 | const create = function(initWares) { 8 | let wares = []; 9 | 10 | if (typeof initWares === 'function') { 11 | wares.push(initWares); 12 | } else if (Array.isArray(initWares)) { 13 | initWares.forEach(ware => { 14 | if (typeof ware === 'function') { 15 | return wares.push(ware); 16 | } 17 | console.warn('inintial middlewares array should be include functions'); 18 | }); 19 | } else if (initWares !== undefined) { 20 | console.warn('inintial argument should be a function or an array'); 21 | } else { 22 | // do nothing 23 | } 24 | 25 | const use = function(fn) { 26 | if (typeof fn === 'function') { 27 | return wares.push(fn); 28 | } 29 | console.warn('use middleware should be a function'); 30 | }; 31 | 32 | const unuse = function(fn) { 33 | wares = wares.filter(func => func !== fn); 34 | }; 35 | 36 | const clean = function() { 37 | wares = []; 38 | }; 39 | 40 | const handle = function() { 41 | return function(req, res, next) { 42 | async.each(wares, function(fn, callback) { 43 | fn(req, res, callback); 44 | }, function(err) { 45 | if (err) { 46 | return console.error(err); 47 | } 48 | 49 | next(); 50 | }); 51 | }; 52 | }; 53 | 54 | const get = function() { 55 | return wares; 56 | }; 57 | 58 | return { 59 | use, 60 | unuse, 61 | clean, 62 | handle, 63 | get 64 | }; 65 | }; 66 | 67 | module.exports = { 68 | create 69 | }; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-dynamic-middleware", 3 | "version": "1.0.0", 4 | "description": "To add, remove dynamic middlewares in runtime for express.", 5 | "main": "index.js", 6 | "scripts": { 7 | "unit": "mocha ./test/index.test.js", 8 | "test": "nyc --reporter=html --reporter=lcov --report-dir=./test/coverage mocha", 9 | "lint": "eslint lib/* test/*" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/lanbomo/express-dynamic-middleware.git" 14 | }, 15 | "keywords": [ 16 | "express", 17 | "middleware", 18 | "dynamic", 19 | "runtime" 20 | ], 21 | "author": "lanbomo", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/lanbomo/express-dynamic-middleware/issues" 25 | }, 26 | "homepage": "https://github.com/lanbomo/express-dynamic-middleware#readme", 27 | "devDependencies": { 28 | "eslint": "^4.19.1", 29 | "eslint-config-standard": "^11.0.0", 30 | "eslint-plugin-import": "^2.11.0", 31 | "eslint-plugin-node": "^6.0.1", 32 | "eslint-plugin-promise": "^3.7.0", 33 | "eslint-plugin-standard": "^3.1.0", 34 | "express": "^4.16.3", 35 | "mocha": "^5.1.1", 36 | "nyc": "^11.7.3", 37 | "sinon": "^5.0.7", 38 | "supertest": "^3.0.0" 39 | }, 40 | "dependencies": { 41 | "async": "^2.6.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 单元测试 3 | */ 4 | 5 | const express = require('express'); 6 | const request = require('supertest'); 7 | const sinon = require('sinon'); 8 | 9 | const assert = require('assert'); 10 | 11 | const dynamicMiddleware = require('../'); 12 | 13 | describe('initial', function() { 14 | this.timeout(6000); 15 | 16 | it('init function middleware', function(done) { 17 | const dynamic = dynamicMiddleware.create(function(req, res, next) { 18 | if (req.query.name === 'lanbomo') { 19 | res.end('hello!'); 20 | } else { 21 | res.status(404).end('404 not found'); 22 | } 23 | }); 24 | 25 | const app = express() 26 | .use(dynamic.handle()); 27 | 28 | request(app) 29 | .get('/') 30 | .expect(404, '404 not found') 31 | .end(function(err) { 32 | if (err) return done(err); 33 | 34 | request(app) 35 | .get('/index?name=lanbomo') 36 | .expect(200, 'hello!', done); 37 | }); 38 | }); 39 | 40 | it('init array middlewares', function(done) { 41 | const auth = function(req, res, next) { 42 | if (req.get('Authorization') === 'Basic') { 43 | next(); 44 | } else { 45 | res.status(401).end('Unauthorization'); 46 | } 47 | }; 48 | 49 | const router = express.Router(); 50 | router.get('/hello', function(req, res) { 51 | res.end('world'); 52 | }); 53 | 54 | router.post('/submit', function(req, res) { 55 | res.json({ 56 | succeed: req.get('Content-Type') === 'application/json' 57 | }); 58 | }); 59 | 60 | const dynamic = dynamicMiddleware.create([auth, router]); 61 | 62 | const app = express() 63 | .use(dynamic.handle()); 64 | 65 | request(app) 66 | .get('/') 67 | .expect(401, 'Unauthorization', function(err) { 68 | if (err) return done(err); 69 | 70 | request(app) 71 | .get('/hello') 72 | .set('Authorization', 'Basic') 73 | .expect(200, 'world', function(err) { 74 | if (err) return done(err); 75 | 76 | request(app) 77 | .post('/submit') 78 | .set('Authorization', 'Basic') 79 | .set('Content-Type', 'application/json') 80 | .expect(200, {succeed: true}, done); 81 | }); 82 | }); 83 | }); 84 | }); 85 | 86 | describe('use and unuse', function() { 87 | describe('use new middleware', function(done) { 88 | this.timeout(6000); 89 | 90 | const dynamic = dynamicMiddleware.create(function(req, res, next) { 91 | if (req.method === 'GET') { 92 | res.send('get'); 93 | } 94 | 95 | next(); 96 | }); 97 | 98 | const app = express() 99 | .use('/', dynamic.handle()); 100 | 101 | it('first middleware', function(done) { 102 | request(app) 103 | .get('/') 104 | .expect(200, 'get', done); 105 | }); 106 | 107 | it('second middleware', function(done) { 108 | setTimeout(function() { 109 | dynamic.use(function(req, res, next) { 110 | if (req.method === 'POST') { 111 | res.send('post'); 112 | } 113 | 114 | next(); 115 | }); 116 | 117 | request(app) 118 | .post('/') 119 | .expect(200, 'post', done); 120 | }, 1000); 121 | }); 122 | 123 | it('third middleware', function(done) { 124 | setTimeout(function() { 125 | dynamic.use(function(req, res, next) { 126 | if (req.method === 'PUT') { 127 | res.send('put'); 128 | } 129 | }); 130 | 131 | request(app) 132 | .put('/') 133 | .expect(200, 'put', done); 134 | }, 2000); 135 | }); 136 | }); 137 | 138 | describe('unuse middleware', function() { 139 | it('unuse', function(done) { 140 | const middlewareAuth = function(req, res, next) { 141 | if (req.query.name === 'lanbomo') { 142 | res.end('hello'); 143 | } 144 | }; 145 | 146 | const dynamic = dynamicMiddleware.create(middlewareAuth); 147 | 148 | const app = express() 149 | .use('/', dynamic.handle()); 150 | 151 | request(app) 152 | .get('/index?name=lanbomo') 153 | .expect(200, 'hello') 154 | .end(function(err) { 155 | if (err) return done(err); 156 | 157 | dynamic.unuse(middlewareAuth); 158 | 159 | request(app) 160 | .get('/index?name=lanbomo') 161 | .expect(404, done); 162 | }); 163 | }); 164 | }); 165 | }); 166 | 167 | describe('get and clean', function() { 168 | it('get middleware', function(done) { 169 | const dynamic = dynamicMiddleware.create([ 170 | function() {}, 171 | function() {} 172 | ]); 173 | 174 | const middlewareLength = dynamic.get().length; 175 | 176 | assert(middlewareLength === 2); 177 | done(); 178 | }); 179 | 180 | it('clean middleware', function(done) { 181 | const dynamic = dynamicMiddleware.create([ 182 | function() {}, 183 | function() {} 184 | ]); 185 | 186 | assert(dynamic.get().length === 2); 187 | 188 | dynamic.clean(); 189 | 190 | assert(dynamic.get().length === 0); 191 | 192 | done(); 193 | }); 194 | }); 195 | 196 | describe('exceptions', function() { 197 | it('init array include value of not function', function(done) { 198 | sinon.spy(console, 'warn'); 199 | 200 | dynamicMiddleware.create([function() {}, null]); 201 | 202 | assert(console.warn.getCall(0).args[0] === 'inintial middlewares array should be include functions'); 203 | 204 | console.warn.restore(); 205 | done(); 206 | }); 207 | 208 | it('init not function or array', function(done) { 209 | sinon.spy(console, 'warn'); 210 | 211 | dynamicMiddleware.create(null); 212 | 213 | assert(console.warn.getCall(0).args[0] === 'inintial argument should be a function or an array'); 214 | 215 | console.warn.restore(); 216 | done(); 217 | }); 218 | 219 | it('init without argument', function(done) { 220 | const dynamic = dynamicMiddleware.create(); 221 | 222 | assert(dynamic.get().length === 0); 223 | 224 | done(); 225 | }); 226 | 227 | it('use not function', function(done) { 228 | sinon.spy(console, 'warn'); 229 | 230 | const dynamic = dynamicMiddleware.create(); 231 | 232 | dynamic.use(null); 233 | 234 | assert(console.warn.getCall(0).args[0] === 'use middleware should be a function'); 235 | 236 | console.warn.restore(); 237 | done(); 238 | }); 239 | 240 | it('handle error', function(done) { 241 | this.timeout(6000); 242 | 243 | sinon.spy(console, 'error'); 244 | 245 | const dynamic = dynamicMiddleware.create(function(req, res, next) { 246 | res.end(); 247 | next('error'); 248 | }); 249 | 250 | const app = express() 251 | .use('/', dynamic.handle()); 252 | 253 | request(app) 254 | .get('/') 255 | .end(function() { 256 | assert(console.error.getCall(0).args[0] === 'error'); 257 | 258 | console.error.restore(); 259 | done(); 260 | }); 261 | }); 262 | }); 263 | --------------------------------------------------------------------------------