meddleware
meddleware copied to clipboard
`mountpath` isn't always a string
Pretty much app.mountpath is just whatever the user had mounted the app on. It's basically one of three things:
- Just a single string. This means it was a "traditional" path
app.use('/path', subApp). - A
RegExpobject. This means it was on a regular expressionapp.use(/^\/path\/?/, subApp) - An array that is any combination of the above two.
app.use(['/path', '/path2', /^\/what(?:\/do)\/?/], subApp)
I hope this helps you, @jasisk :)
@dougwilson you've found the crux of my issue the other day. ;) Since we're here, I'll just talk about what I'm trying to do in this issue.
We use the pattern I've termed the "ephemeral app" pattern all over the place. Meddleware, kraken-js, and swaggerize-express are just three examples. I think it's a great pattern from an adoption standpoint—as far as our users are concerned, they're just mounting another piece of middleware like they've probably done thousands of other times. In return, we get access to the mountpath and parent app instance without the user having to explicitly pass them to us.
You can see how the pattern works from the code however—for completeness—it's basically these steps:
- Create an
appinstance - Register a
mountlistener on theappinstance—listener callback receives the parentappinstance (that which our instance is mounted on) as the first parameter and ...- Store the
mountpathof theappinstance (now accessible asapp.mountpath) - Pop our
appinstance off the parentappinstance's router stack
- Store the
- Return the
appinstance
This works great for kraken and, presumably, most other cases as well.
Where the issue comes up is in mounting other middleware on the parent app on a new mountpath. This is precisely what meddleware tries to accomplish with its route configuration.
Here's an example of how it works in the best case (string mountpath):
var meddleware = require('middleware');
var express = require('express');
var app = express();
var opts = {
myMiddleware: {
route: '/secure',
module: './lib/myMiddleware'
}
};
app.use('/data', meddleware(opts));
In this best-case scenario, the mountpath of meddleware is successfully concatenated with the route for myMiddleware, and we end up with this equivalent behavior:
app.use('/data/secure', require('./lib/myMiddleware')());
The problem is that we can't simply concat regexps. And we could certainly walk arrays and concat strings, ignoring regexps, but I wanted to find a better solution.
So my next attempt was to create a new Router, register all the middleware on that, and mount it on the parent app at mountpath. That way, we can just rely on express' route resolution, and we don't have to try to normalize the path (mountpath + route). Something like this (pseudocode):
function meddleware() {
var app = express();
app.on(mount, onMount);
function onMount(parent) {
var mountpath = app.mountpath;
var router = express.Router();
middlewares.forEach(function (middleware) {
router.use(middleware.route, middleware.module);
});
app.use(mountpath, router);
}
return app;
}
This works great for everything except error-handling middleware. It falls down because of the following behavior:
var app = express();
var otherApp = express();
var router = express.Router();
var ehm = function (err, req, res, next) {
res.send('handled');
};
var badMiddleware = function () { throw new Error(); };
app.use('/', ehm);
router.use(/* '/', */ ehm);
otherApp.use('/', router);
app.get('/', badMiddleware);
otherApp.get('/', badMiddleware);
// get '/' on `app`, get "handled"
// get '/' on `otherApp`, get `finalhandler` output
The behavior is understandable, as we discussed in strongloop/express#2633, but still throws a wrench in the best alternative I could think of for supporting the full array of mountpaths.
So ... any ideas? :grinning:
Would you be interested in setting up a Google Hangout or Skype meeting? If so, send me an email and we can set it up :) I haven't read through all this just yet, just wanted to throw this out there up front while I read everything here.
:+1: email forthcoming. :smile: