I am sorta new to node.js and web programming in general so excuse if I ask strange questions :D
So here is the way I am setting up my express node.js project.
I have a top level app.js simply to redirect traffic to a few subdomains:
var app = module.exports = express.createServer(options);
app.use(express.vhost('blog.localhost', require('./apps/blog/blog.js')));
app.use(express.vhost('app1.localhost', require('./apps/app1/app1.js')));
app.use(express.vhost('login.localhost', require('./apps/login/login.js')));
In each of the sub-apps, that is included via require(), I simply return a new express server:
module.exports = express.createServer(options);
What is the most elegant way to set up a 404 page? When I was just using a single app, I simply used
app.use(function(req, res, next){
res.render('404', {
});
});
But if I use this method, I am going to have to create a 404.jade for every app and I dislike useless duplications in code. Any idea how to share a single 404 logic across multiple vhosts?
You have 3 hosts that require 3 route files. My suggestion is to require in each this 404 route, from an external file.
This way you would have the 404 route for every host, but it will just reside in one place.
Example:
404 route file - 404.js
module.exports = function (app, req, res, options) {
app.get('/404', function(req, res, next) {
res.render('404', { options: options });
});
}
routes for host A
// define other routes here
...
// you pass the app, req, res as params
// the last param is for local vars sent to the view
require('./routes/404.js')(app, req, res, { host: 'HostA' });
Related
I use in my app different routes. All this routes have a topic file. Like:
index.js -> for basic routing in the / level. Like /welcome or
/dashboard
cases.js -> for using edit forms Like /cases/case_create
tcafe.js -> for testing routes like /tcafe/startit
users.js -> for user operation like /users/login or /users/register
In my app I use:
// Routes
app.use('/', require('./routes/index.js'));
app.use('/users', require('./routes/users.js'));
app.use('/cases', require('./routes/cases.js'));
app.use('/tcafe', require('./routes/tcafe.js'));
Ok, now I want to add a 404 handler so I can avoid "Cannot GET /dashboard2"
I added now to the end of all route files:
router.use((req, res, next) => {
next({
status: 404,
message: 'Not Found',
});
});
router.use((err, req, res, next) => {
if (err.status === 404) {
return res.status(400).render('404');
}
if (err.status === 500) {
return res.status(500).render('500');
}
next();
});
now I get a 404 if using "/dashboard2" but all other routes also get a 404
like "/users/login" or "/cases/create_case"
Moving the code to the main js file (server.js) also do not work.
Anybody has a Idea how to protect the whole app with 404 for all routes?
As stated in the official Express FAQ:
All you need to do is add a middleware function at the very bottom of the stack (below all other functions) to handle a 404 response
So in your case, you need to put your 404-handler after all of the router.use() calls.
I have a MEAN stack application and using Node.js and Express.js as back-end API.
Assuming I have a 'comments' route as follow
/* GET /comments listing. */
router.get("/", function(req, res, next) {
Comment.find(function(err, comments) {
if (err) return next(err);
res.json(comments);
});
});
And use it in my server like this:
var commentsRouter = require('./routes/comments');
...
app.use('/comments', commentsRouter);
My question is: Is there a way to prevent users to access http://mrUrl/comments in browser and deny the request with probably 403 Forbidden message but at the same time JavaScript file tries to access the same URL will receive a content message (in the example should be res.json(comments);)
Also, would it be possible to enable such a restriction for all routes once, not for each.
Yes, you can use a middleware.
A middleware is a function you can pass before or after the main function you are executing (in this case, GET comments)
the order of the function location matters, what comes first - executes first, and you implement it like so:
app.use(myBrowsingRestrictionMiddlewareFunction) // Runs
app.use('/comments', commentsRouter);
app.use('/account', accountRouter);
You can also use within a route handler:
app.post('/comments', myMakeSureDataIsAlrightFunction, myMainCreateCommentFunction, myAfterStatusWasSentToClientAndIWishToMakeAnotherInternalActionMiddleware);
The properties req, res, next are passed into the function automatically.
which means, myBrowsingRestrictionMiddlewareFunction receives them and you can use them like so:
export function myBrowsingRestrictionMiddlewareFunction(req, res, next) {
if (req.headers['my-special-header']) {
// custom header exists, then call next() to pass to the next function
next();
} else {
res.sendStatus(403);
}
}
EDIT
Expanding regards to where to place the middleware in the FS structure (personal suggestion):
What I like to do is to separate the router from app.js like so:
app.js
app.use('/', mainRouter);
router.js
const router = express.Router();
router.use(middlewareForAllRoutes);
router.use('/comments', commentsRouter);
router.use(middlewareForOnlyAnyRouteBelow);
router.use('/account', accountRouter);
router.use(middlewareThatWillBeFiredLast); // To activate this, remember to call next(); on the last function handler in your route.
commentsRouter.js
const router = express.Router();
router.use(middlewareForAllRoutesONLYFORWithinAccountRoute);
route.get('/', middlewareOnlyForGETAccountRoute, getAccountFunction);
router.post('/', createAccount);
I have a nodeJS based web app and I want to have a blog subdomain. This is a static (Hexo Based) blog. So I am currently serving my blog on http://localhost:4000/ and an Angular App on http://localhost:4200/, with a project dir like this:
project
|--------Hexo Blog
| |------all Blog files
|
|--------Angular App
|------all ng2 files
I suppose one approach would be to use an Express app.js and vhost to route the blog subdomain to a different port, a bit like this:
app.use(function (req, res, next) {
if (req.headers.host) {
if (typeof req.headers.host === 'string' && req.headers.host.match(/blog\.myDomain\.com/)) { // a simple url match.
proxy.web(req, res, { target: 'http://0.0.0.0:4000/' }); // hexo is running on this host and you can change it.
} else {
next(); // pass the request to other app.
}
}
});
However, I'd rather not add express and vhost as a dependency if it's not required. Is this something I could do with Angular?
I have my application structured with 3 Routes (api, admin, default). Each lives in there own file and has it's own middleware and exports a Route. The problem I am facing is when I want to forward to another route that lives on a different router. Essentially I want to call the same function so that I am not serving up the same view from multiple locations.
I don't want to user res.redirect('/someplace') because I want to be able to pass the req and res objects on to the method.
|-app.js
|-routes
|---admin.js
|---api.js
|---default.js
The routes are required and used in app.js as follows
app.use('/api', require('./routes/api')(passport);
app.use('/admin', require('./routes/admin')(passport);
app.use('/', require('./routes/default')(passport);
Inside of admin if have a situation where I need redirect to login and pass some data
// authenticates all routes for the admin router
router.use(function(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.flashMessage.push('Session expired'); //is lost after redirect
res.redirect('/login');
//do I need to restructure my whole app so that I don't
//have to call res.redirect('login')
});
Any ideas on how to structure this? Do I need to export every method and keep all of my routes in one router file? That doesn't very clean, but if the functions are somewhere else it may be too messy.
You can forward it by calling the next callback ,but only if you do not use any paths.
app.use(function(req, res, next) {
// ... api
next();
});
app.use(function(req, res, next) {
// ... admin
next();
});
Another option is use * that will match all paths:
app.use("*", function(req, res, next) {
var path = req.path; // just example how it can be done
if (path === "/api") {
// ...
path = "/admin";
}
if (path === "/admin") {
// ...
}
});
Edit:
I don't think that express has something like next('/login'); ,so basically function that can forward a request to another path and I don't think that is right to have something like this. If a client ask for /admin you should send this particular page and not the page that is under /login. If you want to send back to a client the login page than just redirect it as you did it in your question. I understand that you want to keep the req, res ,but then is the problem in the proposal/structure of your webapp.
How can I create Express/Connect middleware which wrap each request in its own domain?
This set of slides on Speaker Deck gives a succinct overview:
Domains in node 0.8
Express middleware code from the slides:
var createDomain = require('domain').create;
app.use(function(req, res, next) {
var domain = createDomain();
domain.on('error', function(err) {
// alternative: next(err)
res.statusCode = 500;
res.end(err.message + '\n');
domain.dispose();
});
domain.enter();
next();
});
UPDATE: The approach described below has been implemented in the connect-domain NodeJS module, which can be used in either Connect or Express applications.
As of Express 3, express.createServer is deprecated, and its callback should be converted to a middleware. In the middleware, it's important to add the request and result objects to the request domain so that errors fired by them are handled by the domain error handler.
My middleware looks something like this:
var domain = require('domain');
app.use(function(req, res, next) {
var requestDomain = domain.create();
requestDomain.add(req);
requestDomain.add(res);
requestDomain.on('error', next);
requestDomain.run(next);
});
You can avoid adding the request and response to a request domain if you call http.createServer from within a top-level domain, but the Domain docs seem to indicate that per-request domains are a best practice.
Note that the code above doesn't do any domain clean up actions, such as forcibly disposing the request domain. My middleware chooses instead to pass the error through the middleware stack again to be handled by specific error-handling middleware later on. YMMV.
I've had good luck replacing the stock
var app = express.createServer();
with:
var domainCreate = require('domain').create;
var app = express.createServer(function (req, res, next) {
var domain = domainCreate();
domain.run(next);
});
Then in your middleware you can add properties to process.domain or add additional error handling.
This is a late answer, but check out the express-domain-moddleware module. It automatically creates a new domain for each request. The active domain can be referenced by process.domain in your routes. Here is an example:
//with domain-middleware
app.use(require('express-domain-middleware'));
app.use(app.router);
app.use(function errorHandler(err, req, res, next) {
console.log('error on request %d %s %s: %j', process.domain.id, req.method, req.url, err);
res.send(500, "Something bad happened. :(");
if(err.domain) {
//you should think about gracefully stopping & respawning your server
//since an unhandled error might put your application into an unknown state
}
});
app.get('/error', function(req, res, next) {
db.query('SELECT happiness()', process.domain.intercept(function(rows) {
fs.readFile('asldkfjasdf', process.domain.intercept(function(contents) {
process.nextTick(process.domain.intercept(function() {
throw new Error("The individual request will be passed to the express error handler, and your application will keep running.");
}));
}));
}));
});
The domains are currently deprecated in node:
https://nodejs.org/api/domain.html
For the purpose of 'zoning errors', I've created a library which allows you to write asynchronous code in a nice way: https://github.com/vacuumlabs/yacol . One of its benefits is that you can have domains-like behavior with a very nice semantics; check it out!