Related
So this project was working fine before today, but after a bit of a nooby mistake with git I broke my project, and was unable to recover the commit. After spending some time getting everything fixed, my routes are now broken. My issue is that when calling my API routes now the server hangs for exactly 1 minute, then times out and logs a 404 on the server.
To give some background I'm using this boilerplate. In my debugging process I basically put console.logs everywhere I possibly could, and it looks like all my initialization middleware for express and passport are working fine, and my code is getting to where the routes are defined with no errors.
Middleware with app.use works and all checks out when a request is made, and all console.logs I've put there show fine. The console.logs only stop appearing in the final route definition like in this example:
app.get('/route', function(req, res, next) {
console.log('this never shows');
next();
}, function(req, res, next) {
console.log('this never shows');
})
My actual routes do have a res.send(), this is just an example to show you. All the console.logs I put in the middleware before this route show when the request is made, so it is hanging somewhere in app here.
It's a rather large project, so if you want specific code examples just ask and I'll post it. But I was able to recover all the important files that I had saved somewhere else and I'm pretty sure all my code is back to how it was before now.
edit:
Here is my file with express middleware definitions:
config/express.js
/**
* Module dependencies.
*/
var express = require('express');
var MongoStore = require('connect-mongo')(express);
var flash = require('connect-flash');
var helpers = require('view-helpers');
var swig = require('swig');
var session = require('express-session');
module.exports = function (app, config, passport) {
app.set('showStackError', true);
// should be placed before express.static
app.use(express.compress({
filter: function (req, res) {
return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
},
level: 9
}));
app.use(express.favicon());
app.use(express.static(config.root + '/public'));
app.use('/uploads', express.static(config.root + '/uploads'));
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.status(204).end();
}
else {
next();
}
};
app.use(allowCrossDomain);
// don't use logger for test env
if (process.env.NODE_ENV !== 'test') {
app.use(express.logger('dev'));
}
// set views path, template engine and default layout
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', config.root + '/app/views');
app.set('view cache', process.env.NODE_ENV !== 'development');
app.configure(function () {
// dynamic helpers
// app.use(function(req,res,next){
// req.locals.session = "eeeeeeee";
// next();
// });
// cookieParser should be above session
app.use(express.cookieParser());
// bodyParser should be above methodOverride
app.use(express.bodyParser());
app.use(express.methodOverride());
// express/mongo session storage
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
session({
secret: 'secrettexthere',
saveUninitialized: true,
resave: true,
store: new MongoStore({
url: 'mongodb://localhost/traderdb',
db: 'traderdb',
collection: 'sessions',
auto_reconnect: true
})
})(req, res, next);
}
});
// connect flash for flash messages
app.use(flash());
app.use(function (req, res, next) {
res.locals.session = req.session;
res.locals.req = req;
next();
});
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.initialize()(req, res, next);
}
});
//app.use(helpers('app name'));
//
// use passport session
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.session()(req, res, next);
}
});
// routes should be at the last
app.use(app.router);
// assume "not found" in the error msgs
// is a 404. this is somewhat silly, but
// valid, you can do whatever you like, set
// properties, use instanceof etc.
app.use(function(err, req, res, next) {
// treat as 404
if (~err.message.indexOf('not found')) return next();
// log it
console.error(err.stack);
// error page
res.status(500).render('500', { error: err.stack });
});
// assume 404 since no middleware responded
app.use(function(req, res, next) {
res.status(404).render('404', { url: req.originalUrl, error: 'Not found' })
});
})
}
I also have another file with passport route definitions if you'd like to see that too, but all that is tested and works okay too.
edit 2:
This is my entry point file:
server.js
/**
* Module dependencies.
*/
var express = require('express')
, fs = require('fs')
, passport = require('passport');
/**
* Main application entry file.
* Please note that the order of loading is important.
*/
// Load configurations
// if test env, load example file
var env = process.env.NODE_ENV || 'development'
, config = require('./config/config')[env]
, auth = require('./config/middlewares/authorization')
, mongoose = require('mongoose');
// Bootstrap db connection
mongoose.connect(config.db);
// Bootstrap models
var models_path = __dirname + '/app/models'
fs.readdirSync(models_path).forEach(function (file) {
require(models_path+'/'+file);
});
// bootstrap passport config
require('./config/passport')(passport, config);
var app = express();
// express settings
require('./config/express')(app, config, passport);
// Bootstrap routes
require('./config/routes')(app, passport, auth);
// Start the app by listening on <port>
var port = 3002;
app.listen(port);
console.log('Express app started on port '+port);
// expose app
exports = module.exports = app;
edit 3:
Here are my route definitions:
config/routes.js
var express = require('express');
var path = require('path');
var fileManager = require('express-file-manager');
var mongoose = require('mongoose');
var Session = mongoose.model('Session');
module.exports = function (app, passport, auth) {
var users = require('../app/controllers/users');
var coupons = require('../app/controllers/coupons');
var magazines = require('../app/controllers/magazines');
var zones = require('../app/controllers/zones');
var transactions = require('../app/controllers/transactions');
var favorites = require('../app/controllers/favorites');
var banners = require('../app/controllers/banners');
var reports = require('../app/controllers/reports');
var coverContest = require('../app/controllers/coverContest');
var contactMessage = require('../app/controllers/contactMessage');
app.post('/api/users/login', users.login);
app.post('/api/users/register', users.register);
app.post('/api/users/logout', users.logout);
app.post('/api/users/sendResetEmail', users.sendResetEmail);
app.post('/api/users/changePassword', users.changePassword);
app.post('/api/users/redeemCoupon', isValidAppUser(), users.redeemCoupon);
app.get('/api/users/validate', isLoggedIn(0), function(req, res) {
res.send(req.user);
});
app.post('/api/coupons', coupons.get);
app.post('/api/coupons/import', isLoggedIn(0), coupons.import);
app.post('/api/coupons/remove', isLoggedIn(0), coupons.remove);
app.post('/api/coupons/upload', isLoggedIn(0), coupons.upload);
app.post('/api/transactions', transactions.get);
app.post('/api/allTransactions', isLoggedIn(0), transactions.getAll);
app.post('/api/magazines', magazines.get);
app.post('/api/magazines/import', isLoggedIn(0), magazines.import);
app.post('/api/magazines/remove', isLoggedIn(0), magazines.remove);
app.post('/api/banners', banners.get);
app.post('/api/banners/import', isLoggedIn(0), banners.import);
app.post('/api/banners/remove', isLoggedIn(0), banners.remove);
app.post('/api/favorites', isValidAppUser(), favorites.get);
app.post('/api/favorites/import', isValidAppUser(), favorites.import);
app.post('/api/zones', zones.get);
app.post('/api/zones/add', zones.add);
app.post('/api/zones/addCoupon', zones.addCoupon);
app.post('/api/zones/addMagazine', zones.addMagazine);
app.post('/api/mail/ccSubmit', coverContest.ccSubmit);
app.post('/api/mail/contactSubmit', contactMessage.contactSubmit);
//app.get('/api/reports/siteUsers', reports.siteUsers);
app.get('/auth/facebook', passport.authenticate('facebook', { scope: [ 'email', 'user_about_me'], failureRedirect: '/login' }), users.signin);
app.get('/auth/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/github', passport.authenticate('github', { failureRedirect: '/login' }), users.signin);
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/twitter', passport.authenticate('twitter', { failureRedirect: '/login' }), users.signin);
app.get('/auth/twitter/callback', passport.authenticate('twitter', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/', successRedirect: '/main.html' }));
}
function isLoggedIn(secLvl) {
return function(req, res, next) {
if(req.isAuthenticated() && req.user.secLvl <= secLvl && req.user.google.email.includes('#bizpub36.com')) {
return next();
}
res.redirect('https://accounts.google.com/logout');
}
}
function isValidAppUser() {
return function(req, res, next) {
Session.findOne({ sess_id: req.body.sess_id }).exec(function(err, session) {
if(!err && session) {
next();
}
else {
res.end({ status: 'error', message: 'invalid session' });
}
});
}
}
If app.use works, my guess would be your protocol, is the app.get correct? You issue is otherwise located somewhere else in your code base as your sample runs fine as a single route express app.
It sounds like one of three things:
Somewhere in your middleware chain, you are not calling next() to allow it to advance to the next level of handlers and thus the request just eventually times out waiting for that middleware to finish (this seems to match the symptoms you describe).
Somehow, your app.get() doesn't actually match the route you expect it to or is not specified correctly.
You're using a router, but have not configured it correctly.
But, because you don't get an immediate 404, but rather it times out, it is probably option #1 above.
I have a basic express app and I want to serve one file (after performing some logic) for the default route of /.
Unfortunately I can't use
app.use(function (res, res, next){
*logic here*
res.sendFile(filepath);
});
express.static()
because that will intercept every request and send the filepath for every request.
Is there another way of doing this?
It's enough to check the URI part of url and if it's / then send file.
Check this:
app.use(function (req, res, next) { // first must be Your middleware
if(req.path == '/') {
return res.sendFile('some file');
}
next();
});
app.use(express.static('public')); // and after it You can attach static middleware
or:
app.use(express.static('public'));
app.all('/', function (req, res) {
res.sendFile(filePath);
});
var regexp = /^\/\w+/
app.use(function (req, res, next){
if(!regexp.test(req.path)){
res.sendFile(filepath);
}
});
express.static()
this may work comment your requirement
suppose I have a simple express js application like the following:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
return res.json({ hello: 'world' });
});
module.exports = app;
I want to be able to go to the command line, require the app, start the server and simulate a request. Something like this:
var app = require('./app');
app.listen(3000);
app.dispatch('/') // => {hello:"world"}
You can use run-middleware module exactly for that. This is working by creating new Request & Response objects, and call your app using those objects.
app.runMiddleware('/yourNewRoutePath',{query:{param1:'value'}},function(responseCode,body,headers){
// Your code here
})
More info:
Module page in Github & NPM;
Examples of use run-middleware module
Disclosure: I am the maintainer & first developer of this module.
These two options worked for me without any error:
option 1
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
next();
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
option 2
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
app._router.handle(req, res, next);
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
Express : 4.15.4
No extra library or npm module is required
this solution works perfectly by using express 4.16
(and optionally -express promise router , which is great wrapper for error handling)
its straightforward, without using router inside a router nor re-write the request, like in the other suggested answers
just change the URL in the request and return it to router handle function
const router = require('express-promise-router')();
router.post('/signin', function(req, res , next) {
if (req.body.Username === 'admin') {
req.url = '/admin/signin'
router.handle(req, res, next)
}
else { // default doctor
req.url = '/doctors/signin'
router.handle(req, res, next)
}
});
router.post('/doctors/signin',someCallback1)
router.post('/admin/signin',someCallback1)
As far as I am aware, there isn't a way to switch to a specific route internally, but there is a way to flag the request, then move on to the next route:
app.use((req, res, next) => {
if("nextRouteCondition"){
req.skip = true;
return next();
}
})
This may allow you to accomplish what you want to do.
My code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'en',
directory: __dirname + '/locales',
cookiename: 'locale'
});
app.configure(function () {
app.use(i18n.init);
})
app.get('/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
res.redirect('/');
});
The problems are two:
In template does not work output through
__("Name Key")
When the transfer is not directly through value, do not change text. There are all languages files
res.render('index', {name: res.__('name') });
But do not switch languages
/* ----- */
The resulting code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'ru',
directory: __dirname + '/locales'//,
cookiename: 'locale'
});
app.use(function (req, res, next) {
res.locals.__ = res.__ = function() {
return i18n.__.apply(req, arguments);
};
next();
});
app.get('/i18n/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
if (req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
});
This works
In your templates, assuming that you are using JADE you must embrace with #{}, like #{__("Your key")}
I didn't understand your second question, mind rephrasing?
Anyways, if you wanna use i18n from a controller, you must do: res.render('index', {name: res.i18n.__('name') });
When switching the language you must:
1- save user preference anywhere (session or cookies, for example)
app.get("/i18n/:locale", setLocale);
function setLocale(req, res, next){
req.session.locale = req.params.locale;
if(req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
}
2- re-apply this change at every request (simply use a middleware):
var app = require("express")();
app.use(function(req, res, next){
if(req.session.locale) //check if user has changed i18n settings
req.i18n.setLocale(req.session.locale);
})
app.get("/", function(req, res, next){
res.render('index', {name: res.i18n.__('name') });
});
app.listen(8000);
Based on the answer from #renatoargh, I had to make some modifications, but here is the final block that seemed to get things working for me.
// configure i18n
i18n.configure({
locales : [
'en',
'zh'
],
directory : __dirname + '/locales'
});
// configure app
app.configure(function () {
// initialize session support
app.use(express.cookieParser());
app.use(express.cookieSession({secret: uuid.v4()}));
// initialize i18n
app.use(i18n.init);
// set locale (on every request), if session locale exists
// otherwise use default browser setting
app.use(function (req, res, next) {
// check if user has changed i18n settings
if (req.session.locale) {
i18n.setLocale(req, req.session.locale);
}
next();
});
});
// allow MANUAL locale selection
app.get("/i18n/:locale", function (req, res) {
req.session.locale = req.params.locale;
// go back to referrer OR root (/)
res.redirect('back');
});
NOTE in the the configuration, I've set the directory. For some reason this was necessary, even though the docs state that its the default.
Also note that I'm using secret: uuid.v4(), but you can just hard-code this value for persistence across server restarts.
I'm currently working on an application built with Express (Node.js) and I want to know what is the smartest way to handle different robots.txt for different environments (development, production).
This is what I have right now but I'm not convinced by the solution, I think it is dirty:
app.get '/robots.txt', (req, res) ->
res.set 'Content-Type', 'text/plain'
if app.settings.env == 'production'
res.send 'User-agent: *\nDisallow: /signin\nDisallow: /signup\nDisallow: /signout\nSitemap: /sitemap.xml'
else
res.send 'User-agent: *\nDisallow: /'
(NB: it is CoffeeScript)
There should be a better way. How would you do it?
Thank you.
Use a middleware function. This way the robots.txt will be handled before any session, cookieParser, etc:
app.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send("User-agent: *\nDisallow: /");
});
With express 4 app.get now gets handled in the order it appears so you can just use that:
app.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
1. Create robots.txt with following content :
User-agent: *
Disallow: # your rules here
2. Add it to public/ directory.
3. If not already present in your code, add:
app.use(express.static('public'))
Your robots.txt will be available to any crawler at http://yoursite.com/robots.txt
Looks like an ok way.
An alternative, if you'd like to be able to edit robots.txt as regular file, and possibly have other files you only want in production or development mode would be to use 2 separate directories, and activate one or the other at startup.
if (app.settings.env === 'production') {
app.use(express['static'](__dirname + '/production'));
} else {
app.use(express['static'](__dirname + '/development'));
}
then you add 2 directories with each version of robots.txt.
PROJECT DIR
development
robots.txt <-- dev version
production
robots.txt <-- more permissive prod version
And you can keep adding more files in either directory and keep your code simpler.
(sorry, this is javascript, not coffeescript)
Here is what I use
router.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send(
`User-agent: *
Disallow: /admin`);
});
For choosing the robots.txt depending the environment with a middleware way:
var env = process.env.NODE_ENV || 'development';
if (env === 'development' || env === 'qa') {
app.use(function (req, res, next) {
if ('/robots.txt' === req.url) {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
} else {
next();
}
});
}
This is what I did on my index routes. You can just simply write down in your codes what I does given down below.
router.get('/', (req, res) =>
res.sendFile(__dirname + '/public/sitemap.xml')
)
router.get('/', (req, res) => {
res.sendFile(__dirname + '/public/robots.txt')
})
I use robots.txt as a normal file for Prod, and a middleware for other envs.
if(isDev || isStaging){
app.use('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
}
app.use(express.static(path.join(__dirname, 'public')));
Focusing more on the most convenient and simple solution instead of the "best" or "smartest". I simply added the following to the server.ts file.
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
What this does is create a robots.txt file on the fly and sends it whenever the /robots.txt file is called for.
Now to get this to work, the code fragment must be placed before the other server.get function calls (so it takes priority). I'm implementing Express with Angular, for which the full code fragment for me ended up being:
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/sophisticatedPrimate/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
app.use(express.static('public'))
app.use('/images', express.static('public/images'))
app.use('/videos', express.static('public/videos'))