Created
May 6, 2015 07:30
-
-
Save avishnyak/d4663bc75fa6ce832dd4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Minimal middleware implementation | |
// Works in node and the browser | |
// Inspired by https://github.com/h2non/midware | |
// Usage examples are at the bottom... | |
function middleware(ctx) { | |
let calls = []; | |
let errorHandlers = []; | |
var ctx = ctx || null; | |
function addMiddleware(...args) { | |
// Optimize for the most common case | |
if (args.length === 1 && typeof args[0] === 'function') { | |
this.push(args[0]); | |
return; | |
} | |
// Handle the general case of many functions passed in | |
args | |
.filter(fn => { return typeof fn === 'function'; }) | |
.forEach(fn => { this.push(fn); }); | |
} | |
function use() { | |
addMiddleware.apply(calls, Array.prototype.slice.call(arguments)); | |
} | |
function err(...args) { | |
addMiddleware.apply(errorHandlers, Array.prototype.slice.call(arguments)); | |
} | |
function run(...args) { | |
var done; | |
let stack = calls.slice(); | |
var lastExecutedFn; | |
if (typeof args[args.length - 1] === 'function') { | |
done = args.pop(); | |
} | |
if (!stack.length) { | |
callDone(); | |
return; | |
} | |
args.push(next); | |
function exec() { | |
try { | |
lastExecutedFn = stack.shift(); | |
lastExecutedFn.apply(ctx, args); | |
} | |
catch (e) { | |
// Call next() function with an error | |
next(e); | |
} | |
} | |
function callDone(error) { | |
if (done) { | |
done.call(ctx, error); | |
} | |
} | |
function next(error, end) { | |
if (end || !stack.length) { | |
stack = null; | |
callDone(error); | |
return; | |
} | |
if (error) { | |
args.push(error); | |
stack = errorHandlers.slice(); | |
} | |
exec(); | |
} | |
exec(); | |
// Detect missing call to next | |
if (stack !== null && typeof lastExecutedFn === 'function') { | |
console.warn(`Missing call to next()? Use next(null, true) to stop further processing in middleware: \n${lastExecutedFn.name == '' ? lastExecutedFn.toString() : lastExecutedFn.name}`); | |
} | |
}; | |
return { | |
use, | |
err, | |
run | |
}; | |
} | |
// Using a context is optional | |
var context = { entity: 'test' }; | |
// Create an instance of middleware | |
var app = middleware(context); | |
// CASE 1: Well-behaved middleware function | |
app.use(function (next) { | |
console.log(this.entity); | |
next(); | |
}); | |
// CASE 2: Multiple well-behaved middleware functions in a single declaration | |
app.use(function (next) { | |
console.log('Thing 1'); | |
next(); | |
}, function (next) { | |
console.log('Thing 2'); | |
next(); | |
}); | |
// CASE 3: Middleware signals error and prevents further execution | |
app.use(function (next) { | |
next('Error'); | |
}); | |
// CASE 4: Middleware throws error and prevents further execution | |
app.use(function (next) { | |
throw new Error('Test'); | |
next(); | |
}); | |
// CASE 5: Missing next() call prevents further execution | |
app.use(function (next) { | |
// missing next(); | |
}); | |
// CASE 6: Error handler processes the error and passes it on | |
app.err(function errLogger(next, err) { | |
console.error(err); | |
next(err); | |
}); | |
app.run(function finalizer(err) { | |
console.log(typeof err === 'undefined' ? 'Success or handled error' : 'Error'); | |
}); | |
// Middleware with parameters | |
var app2 = middleware(); | |
app2.use(function (req, res, next) { | |
console.log(req, res); | |
next(); | |
}); | |
app2.run({ url: 'http...' }, { status: 200 }, function finalizer2(err) { | |
console.log(typeof err === 'undefined' ? 'Success' : 'Error'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment