I'm trying to include a middleware (passport-http-bearer) in MEAN.js, however it uses a different routing syntax than Express 4.
Express API sytnax is:
app.get('/', function(req, res){
res.send('hello world');
});
In MEAN.js routes are defined like this:
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
How do I include a middleware in the MEAN.js router (in my case passport-http-bearer to check for a token)?
http-bearer's example implementation as middleware is:
app.get('/profile',
passport.authenticate('bearer', { session: false }),
function(req, res) {
res.json(req.user);
});
How should I do this in MEAN.js?
For anyone else ending up here trying to figure out how to do this, here is how it can be done:
app.route('/articles')
.get(passport.authenticate('bearer', { session: false }), articles.list)
.post(passport.authenticate('bearer', { session: false }), articles.create);
Or to make it look nicer, the whole auth function could be put in users.authorization.server.controller.js and called woith something like this:
app.route('/articles')
.get(users.requiresToken, articles.list)
.post(users.requiresToken, articles.create);
Related
I have multiple routes in my express application for different prefix. Each prefix's routes are defined in separate files.
const routes = require('./server/routes');
app.use('/api', routes.apirouter);
app.use('/', routes.webrouter);
where './server/routes.js' is:
module.exports.apirouter = require('./api');
module.exports.webrouter = require('./webroutes');
Hence currently, I am handling and defined all routes with /api prefix in 'api.js' and all other routes are defined in 'webroutes.js'
Now similarly I need to define all the routes with prefix 'fetch-' to a separate js file 'fetch.js', hence http://localhost/fetch-one and http://localhost/fetch-two need to be defined in fetch.js
However the following code is not working for /fetch-one:
const routes = require('./server/routes');
app.use('/api', routes.apirouter);
app.use('/', routes.webrouter);
app.use('/fetch-*', routes.fetchrouter);
routes.js:
module.exports.apirouter = require('./api');
module.exports.webrouter = require('./webroutes');
module.exports.fetchrouter = require('./fetch');
fetch.js:
Route defined for /fetch-one and /fetch-two separately in fetch.js
var fetchRouter = require('express').Router();
fetchRouter.get('/fetch-one', function(req, res, next) {
// localhost/fetch-one not passed control here
});
fetchRouter.get('/fetch-two', function(req, res, next) {
// localhost/fetch-two not passed control here
})
module.exports = fetchRouter;
The problem is that once you've done this:
app.use('/fetch-*', routes.fetchrouter);
Then, the /fetch-* part of the path has been removed from the routing for the fetchrouter. So, when you then do:
fetchRouter.get('/fetch-one', ...)
That won't match because /fetch-one has already been removed from the routing path. The URL would have to have been /fetch-xxx/fetch-one for that to match.
The simplest design would be to change your paths so that the URLs are /fetch/one and /fetch/two which is much more in line with how Express routers work. Then you'd go with:
app.use('/fetch', routes.fetchrouter);
And, have routes in that router for
app.get('/one, ...)
app.get('/two, ...)
That's the URL design that lines up the cleanest with the way Express routers work the simplest.
If you're going to stay with the /fetch-one URL design, then another idea would be to let the fetchRouter look at all top level URLs:
app.use('/', fetchRouter);
And, then have it only have routes for the top level routes you want it to look at. Express will then continue look for other routes that match if it doesn't handle things:
app.get('/fetch-one', ...);
app.get('/fetch-two', ...);
You need to make sure there are no greedy top level routers that take all requests and make sure that this router only takes the request that it needs so that other top level routes get a chance to get matched.
If you really want to stay with the /fetch-* design for the router, then you can do a bit of your own routing and URL comparison:
app.use('/fetch-*', routes.fetchrouter);
Then, in the fetchrouter:
app.get("/", function(req, res, next) {
switch(req.baseUrl) {
case "/fetch-one":
// process /fetch-one here
break;
case "/fetch-two":
// process /fetch-two here
break;
default:
next();
}
});
I thought of one other option that uses the Express parameters where you would just use a handler function for the fetch routes instead of a router:
app.use('/fetch-:id', routes.fetchHandler);
Then, in the fetchHandler:
function fetchHandler(req, res, next) {
switch(req.params.id) {
case "one":
// process /fetch-one here
break;
case "two":
// process /fetch-two here
break;
default:
next();
}
});
Instead of a big switch, you can make it table driven too which is probably cleaner if you have a lot of routes:
app.use('/fetch-:id', routes.fetchHandler);
Then, fetchHandler would be an exported function:
const routeTable = {
one: routeOne,
two: routeTwo,
three: routeThree,
....
};
function fetchHandler(req, res, next) {
let fn = routeTable[req.params.id];
if (fn) {
fn(req, res, next);
} else {
next();
}
});
function routeOne(req, res, next) {
...
}
function routeTwo(req, res, next) {
...
}
function routeThree(req, res, next) {
...
}
I try to force my Sails.js WebApp which is hosted on heroku(no nginx installed) from http:// to https:// and use this express middleware in my sail.js app:
In Express it would look like this:
app.use(forceDomain({
hostname: 'www.example.com',
port: 4000,
protocol: 'https'
}));
I tried to use it in my config/http.js file in my sails.js app:
middleware: {
forceDomain: function (req, res, next) {
forceDomain({
hostname: 'www.myurl.com',
port: 4000,
protocol: 'https'
});
next();
},
order: [
'forceDomain',
...
}
I don't understand exactly how to use this "app.use()" thing in sails.js.
It is here explained, but I didn't really understand. What I now have doesn't work(no errors, but also no redirecting). How can I fix this?
Installed this module - doesn't work either.
Here is the solution how to force ssl on a sails.js app running on heroku without nginx and without external modules:
In the config/http.js file there is an example of custom middleware:
****************************************************************************
* Example custom middleware; logs each request to the console. *
****************************************************************************
myRequestLogger: function (req, res, next) {
console.log("Requested :: ", req.method, req.url);
next();
}
I made my own custom middleware function which controls if the request is secure, and if not, it redirects the request to https:// or if its a websocket request it redirects the request to wss://
order: [
...
'forceSSL',
...
],
forceSSL: function (req, res, next) {
if (req.isSocket) {
return res.redirect('wss://' + req.headers.host + req.url);
} else if (req.headers["x-forwarded-proto"] == "http") {
return res.redirect('https://' + req.headers.host + req.url);
} else {
next(); //it's already secure
}
}
No need for extern modules or hookups. just that function and it works.
Every value in the sails.config.http.middleware dictionary (besides order) should be an Express-style middleware function -- that is, a function that accepts req, res, and next, which is exactly what the forcedomain module returns. The issue in your code is that you're wrapping that function in another function instead of just returning it. Try:
middleware: {
forceDomain: forceDomain({
hostname: 'www.myurl.com',
port: 4000,
protocol: 'https'
}), // <-- the call to `forceDomain()` returns a configured Express middleware fn.
order: [
'forceDomain',
...
}
This assumes that you have var forceDomain = require('forcedomain') at the top of that file!
I'm working in a restful service using express.js and i want to enhance the req and res variables so for example you could write something like
app.use(function (req, res, next) {
res.Ok = function (data) {
res.status(200).send(data);
};
res.InternalError = function (err) {
res.status(500).send(err);
};
});
And later
router.get('/foo', function (req, res) {
res.Ok('foo');
})
This will send 'foo' in the body of the response and set the status code to 200 and is working perfectly.
My first question is if it is possible to add such functionality without a middleware function, lets say in a property or the prototype of the app variable?
The second question is if there are performance issues if you add many functionality with middleware functions at the app level. Are this functions attached to the request and response object per request or once on the application startup?
I know the Sails framework already do this but I'm wondering if they use middleware functions as well.
I keep digging and turns out that the request and response object are exposed in express using the __proto__ property.
var express = require('express'),
app = express();
app.response.__proto__.foo = function (data) {
this.status(200).send(data);
};
And later in the router
router.get('/foo', function (req, res, next) {
res.foo('test');
});
This will print test in your browser so it is possible to add functionality without using any middleware.
Note: I'm sure there are some drawbacks to this approach (overwriting express predefined properties, for example) but for testing purposes and adding very simple functionality I think is slightly better in terms of performance.
I'm not aware of any other way than using middleware. But in my opinion you could do the following to achieve nearly the same thing.
// Some Route
router.get('/foo', function(req, res, next) {
// ...
if(err) {
res.status(500);
return next(err);
}
return res.send('ok');
});
// Another route
router.get('/bar', function(req, res, next) {
// ...
if(badUserId) {
res.status(400);
return next('Invalid userId.');
}
req.result = 'hello';
return next();
});
router.use(function(req, res) {
// I prefer to send the result in the route but an
// approach like this could work
return res.send(req.result);
});
// Error Middleware
router.use(function(err, req, res, next) {
if(res.statusCode === 500) {
// Log the error here
return res.send('Internal server error');
} else {
return res.send(err);
}
});
Does anybody know proper way to pass context to sails controllers action?
Here is my case why I want to do it:
--- AbstractPageController.js ---
module.exports = {
_getPageData: function(req, res) {
return {
data: {
title: this._getTitle(req, res), // if do nothing "this" is global object
menu: this._getMenu(req, res)
}
};
},
_getTitle: function(req, res) { return 'Cool Page'; },
_getMenu: function(req, res) { return [{ href: '/logout' }]; }
};
--- ConcretePageController.js ---
var _ = require('lodash');
_super = require('./AbstractPageController.js');
module.exports = _.merge({}, _super, {
'main': function(req, res) {
res.view('pageTemplate', this._getPageData(req, res));
},
_getTitle: function(req, res) {
return 'Absolutely - ' + _super._getTitle(req, res);
},
_getMenu: function(req, res) {
return [{ href: '/main/'}].concat(_super._getMenu(req, res));
}
});
That's why I need context.
For this particular case I found this solution:
--- routes.js ---
var concreteController = require('../api/controllers/ConcretePageController.js');
module.exports.routes = {
'/concrete_page': function(req, res) { concreteController.main(req, res); }
}
But it seems a little bit ugly and sails hooks (for example policies) stop works.
I was thinking about the other way. The main point of this, is to move all logic to services and to use a simple inheritance. But this seems strange for me too
Any ideas about a better way to reach the cases I have wrote?
P.S. All code I have wrote above is just an example.
I think you whant to pre-set some data to send to view template, right?
is yes you can use one Before hook ( like midleware ) or sails police
I use the sails hook for preload sails features from npm modules in we-plugin https://github.com/wejs/we-plugin
Check this hook for how load user locale in all requests after controllers :
Link: https://github.com/wejs/we-example/blob/master/api/hooks/we-locale/index.js#L15
You should just reference the controller directly
sails.controllers.yourControllerName.getTitle()
https://stackoverflow.com/a/20994036/1821723
URL pattern is www.example.com/product-name/pid_010101. URL's first segment is Product name and second segment is Product number. My app router code is below
app.get("/:name?/:id", routes.index);
Now my all urls redirect to same page. For example some url like www.example.com/homepage/banner.html also redirect to www.example.com/product-name/pid_010101
Need to add some Filters in router. How to route the url to correct page?
I assume your routes will be:
/coffeemaker/pid_0101222
/bluemarker/pid_121121
etc. ?
You can use regular expressions for this OR
this is an example of in method filtering:
app.get('/rest/:collection', function(req, res) {
return routes.rest[req.params.collection](req, res);
});
in routes object:
exports.rest = {
tweets: function(req, res) {
return twitter.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
pics: function(req, res) {
return instagram.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
repos: function(req, res) {
return github.data.load(function(data) {
return res.json(data);
}, req);
},
links: function(req, res) {
return delicious.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
}
};
:colection is then tweets, pics , or links string
Can you make a list of all your pages?
Maybe I can help you with your routes...
This is because of /homepage/banner.html complies to your route as well.
Therefore you must also specify those routes you want to trigger.
Say for example you have a welcome page.(/welcome/guest) or something like that.
You can add another route above the general route. specific for that page.
app.get("/welcome/guest", routes.welcome);
Now this is too much work for all your pages. So you can avoid this with a couple of techniques, one would be to put a static part in the url say:
app.get("/product/:name?/:id", routes.product);
I don't really get your problem. Is /homepage/banner.html a static page? Middlewares work like a filter, you can simply put express.static above express.router:
app.use(express.static('public'));
...
app.use(app.router);
app.get("/:name?/:id", routes.index);
banner.html is in (APP_DIR)/public/homepage/banner.html
Write own middleware function that handles the legacy URLs, and place it above the Express router.
app.use(function(req, res, next) {
if (legacySystemHandles(req.url)) {
// do legacy stuff
}
else next(); // pass to the next middleware function
});
app.use(app.router);