var express = require('express');
var app = express();
app.get('*', function (req, res) {
var host = req.get('Host');
return res.redirect(['https://', host, req.url].join(''));
});
var server = app.listen(8080, function () {
console.log('starting');
});
I have a simple script that redirects http to https. This is working fine except when there is a malformed url for example: website.com/%c0%ae%c0%ae. It displays something like:
URIError: Failed to decode param '/%c0%ae%c0%ae'
at decodeURIComponent (native)
at decode_param (/...<PROJECT DIRECTORY>.../node_modules/express/lib/router/layer.js:167:12)
at Layer.match (/.../node_modules/express/lib/router/layer.js:143:15)
at matchLayer (/.../node_modules/express/lib/router/index.js:557:18)
at next (/.../node_modules/express/lib/router/index.js:216:15)
at expressInit (/.../node_modules/express/lib/middleware/init.js:33:5)
at Layer.handle [as handle_request] (/.../node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/.../node_modules/express/lib/router/index.js:312:13)
at /.../node_modules/express/lib/router/index.js:280:7
at Function.process_params (/.../node_modules/express/lib/router/index.js:330:12)
It's not nice when a user can randomly see where my project files are in the server. Any way to handle this error?
Thanks #Oleg for the tip. But somehow your solution wasn't logging error for me. Here's what I have come up with:
var express = require('express');
var app = express();
app.use(function(req, res, next) {
var err = null;
try {
decodeURIComponent(req.path)
}
catch(e) {
err = e;
}
if (err){
console.log(err, req.url);
return res.redirect(['https://', req.get('Host'), '/404'].join(''));
}
next();
});
app.get('*', function (req, res) {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
});
var server = app.listen(8080, function () {
console.log('Starting');
});
Possible workaround:
var express = require('express');
var app = express();
app.get('*', function (req, res) {
// redirect regular paths
var host = req.get('Host');
return res.redirect(['https://', host, req.url].join(''));
});
// your express error handler
app.use(function(err, req, res, next) {
// in case of specific URIError
if (err instanceof URIError) {
err.message = 'Failed to decode param: ' + req.url;
err.status = err.statusCode = 400;
// .. your redirect here if still needed
return res.redirect(['https://', req.get('Host'), req.url].join(''));
} else {
// ..
}
// ..
});
var server = app.listen(8080, function () {
console.log('starting');
});
var express = require('express');
var app = express();
// handles 400 error
app.use((err, req, res, next) => {
if (!err) return next();
return res.status(400).json({
status: 400,
error: 'OOps! Bad request',
});
});
Edited:
The code snippet should be placed as the last route, it checks if there is an error that was skipped by other routes, which obviously there is, and sends a default response. This error happens when you add % as last character of an API endpoint..
Related
I am writing custom middleware(s) to send request data(like request path, response code, request timestamp, etc) to a remote server on every request. I'm unable to figure out the ordering of my middlewares.
I have created 2 middlewares,
a) process_request (m1) --> this middleware just adds a timestamp to the request object to register when the request was received.
b) process_response (m2) --> this middleware posts the required data to the remote server
m1
function process_request(req, res, next) {
req.requestTime = Date.now()/1000;
next();
}
m2
function process_response(req, res, next) {
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.response_code = res.statusCode;
send_perf_request(request_obj); // sends remote https request not shown here
}
I can think of two ordering options in my app.js:
Order 1:
m1
route 1
route 2
...
route n
m2
404 request handler middleware
Order 2:
m1
route 1
route 2
...
route n
404 request handler middleware
m2
404 request handler middleware
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
The problem with order 1 is that I won't be able to catch 404 requests, which I want to.
The problem with order 2 in all in request the responseCode=404 as the 404 request handler middleware is doing that.
I am new to node.js & confused if I am thinking about this the right way.
My app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const custom_middleware = require("custom_middleware");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(custom_middleware.process_request); //*calling middleware
app.use('/', indexRouter);
app.use('/users', usersRouter);
//catch 404 and forward to error handler
// app.use(function(req, res, next) {
// console.log("in 404 handler");
// //next(createError(404, "this page doesn't exist;"));
// next();
// });
// error handler
app.use(function(err, req, res, next) {
// this is called only in case of errors
// set locals, only providing error in development
console.log("in error handler");
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
console.log(res.statusCode);
//res.render('error');
next(err);
});
app.use(custom_middleware.process_exception); //*calling middleware
module.exports = app;
Custom middleware file
function process_request(req, res, next) {
// this middleware adds request timestamp
console.log("in process request middleware");
req.requestTime = Date.now()/1000;
res.on("finish", function (){
// this middleware collects performance data
console.log("in process response middleware");
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.ip_address = ip.address();
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.requester = null;
request_obj.response_code = res.statusCode;
console.log(request_obj.response_code);
send_perf_request(request_obj);
})
next();
}
function process_exception(err, req, res, next) {
// this middleware collects exception data
console.log("in process exception middleware");
const error_obj = {};
error_obj.path = req.protocol + "://" +
req.hostname + req.originalUrl;
error_obj.exception = "error occured";
error_obj.traceback = err.stack;
error_obj.user = null;
error_obj.ip_address = ip.address();
send_exception_request(error_obj);
next();
}
My routes/index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
throw new Error('this does not exist'); // error manually triggered
res.status(500);
});
module.exports = router;
As mentioned in the comments, abstract the middlewares to avoid defining a set of middlewares on each route.
To replace m1 Create a global middleware, which you define before all your other middlewares which sets res.on("finish", function () {}) event handler to do something when the route is done. I'm pretty sure this is the only event you will get the actual correct res.statusCode if you're doing res.status() anywhere.
Then move your 404 error handler logic into the main error handler, you can then check for status code and all that good stuff before logging and responding. You can also use req.xhr to determine how to respond.
Also, you can use a catch-all to pick up on route 404's: app.get('*', function (req, res, next) { then fire off an error which the error handler handles.
Here is an example putting it all together:
const express = require('express')
const app = express()
// logger middleware
app.use((req, res, next) => {
// set something
req.requestTime = Date.now() / 1000;
// gets fired after all routes have been handled
res.on("finish", function () {
//
req.finishTime = Date.now() / 1000;
// do something with req, res objects
console.log('[in logger] originalUrl:', req.originalUrl)
console.log('[in logger] status code:', res.statusCode)
})
next()
})
// apply routes
// ...
app.get('/', (req, res, next) => {
try {
// example: look for something, which is not found
const mock = false
if (!mock) {
let err = new Error('Mock was not found!')
err.status = 404
throw err
}
res.send('Hello World!')
} catch (err) {
next(err)
}
})
// app.get('/foo', ...
// not found route (catch all)
app.get('*', (req, res, next) => {
let err = new Error('Page not found!')
err.status = 404
next(err)
})
// error handler (will catch everything even uncaught exceptions)
app.use((error, req, res, next) => {
//
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
res.header('Expires', '-1')
res.header('Pragma', 'no-cache')
// set status from error
res.status(error.status || 500)
if (req.xhr) {
// is ajax call, send error as json
res.json({
error: error.name || 'Server error',
status: error.status || 500,
message: error.message || 'Internal server error'
})
} else {
// render a view instead (would need to add a view engine)
res.render('error/' + (error.status || 500), error)
}
})
app.listen(3000, function () {
console.log('App listening on port 3000!')
})
I am a beginner studying Nodejs.
I have recently studied node middleware and have created a simple game using middleware.
The purpose of the generated code is to respond to hello by connecting as root and then respond to the browser with 50% probability through the middleware.
However, I get the following error:
I did a search and found that res.send is not available after next ().
Is that correct?
But I could not figure out why and I did not realize why the code did not work.
code
const express = require('express');
var app = express();
app.use('/', (req, res, next) =>{
res.send('hello');
next();
});
app.use((req, res, next) => {
if (+new Date() % 2 === 0) {
console.log('continue');
res.send('lucky!');
next();
} else {
console.log('failed');
res.send('end');
}
});
app.use((req, res, next) => {
if (+new Date() % 2 === 0) {
console.log('continue');
res.send('lucky!');
next();
} else {
console.log('failed');
res.send('end');
}
});
app.listen(3000, () => console.log(`Example!`))
error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
const express = require('express');
var app = express();
app.use(/^\/$/, (req, res, next) =>{
res.send('hello');
return;
});
app.use((req, res, next) => {
if (+new Date() % 2 === 0) {
console.log('continue');
res.send('lucky!');
next();
} else {
console.log('failed');
res.send('end');
}
});
app.listen(3000, () => console.log(`Example!`))
you cannot use res.send() twice
res.send() = Sends the HTTP response.
platform
node: v-4.4.5 koa: v-2.0.0 koa-router: v-7.0.0
here is my code
///<reference path="../typings/tsd.d.ts" />
//导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
var Koa = require('koa');
var router=require("koa-router")();
// 创建一个Koa对象表示web app本身:
var app = new Koa();
// parse request body:
//add url-route:
router.get('/hello/:name', (ctx, next) => {
var name = ctx.params.name;
ctx.response.body =`<h1>Hello, ${name}!</h1>`;
});
router.get('/', function *(ctx, next) {
ctx.response.body = '<h1>Index</h1>';
});
router.all('/login', function *() {
this.redirect('/');
this.status = 301;
});
app.use(function (ctx,next){
this.body = `Invalid URL!!!${ctx.request.method} ${ctx.request.url}`;
ctx.response.type = 'text/html';
ctx.response.body = this.body;
});
app.use(router.routes())
.use(router.allowedMethods());
// 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');
when I Brower http://localhost:3000/,output 'Invalid URL!!!'. tell me why can't match '/' router ? thank you!
you can use '*' to match urls like 404 or invalid links:
router.all('*', function(ctx, next) {
//
});
upgrade your node version to 7.6.0. koa2 work on >=7.6.0
and code like
Koa=require('koa')
var app = new Koa();
router = require('koa-router')()
// parse request body:
//add url-route:
router.get('/hello/:name', (ctx, next) => {
var name = ctx.params.name;
ctx.response.body =`<h1>Hello, ${name}!</h1>`;
});
router.get('/', function (ctx, next) {
console.log(ctx.request);
ctx.body = '<h1>Index</h1>';
});
router.all('/login', function (ctx) {
ctx.redirect('/');
ctx.status = 301;
});
app.use(function (ctx,next){
this.body = `Invalid URL!!!${ctx.request.method} ${ctx.request.url}`;
ctx.response.type = 'text/html';
ctx.response.body = this.body;
next()
});
app.use(router.routes())
.use(router.allowedMethods());
// 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');
Following is my server file. I am making 2 calls, one post and one get. It works fine at times. But gives an error of : Can't set headers after they are sent. Does this have anything to do with my client side code?
server.js
var express = require('express')
var mongoose = require('mongoose')
var path = require('path')
var bodyParser = require("body-parser")
var cors = require("cors")
var app = express()
var port = process.env.PORT || 3000
var Url = require("./data/url-schema");
//Express request pipeline
app.use(express.static(path.join(__dirname,"../client")))
app.use(bodyParser.json())
app.use(cors());
/*
Your server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /dashboard your web server will get a request to /dashboard. You will need it to handle that URL and include your JavaScript application in the response.
*/
app.get('*', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/:code', function(req, res) {
console.log("reg", req.params.code)
Url.findOne({code:req.params.code}, function(err, data){
console.log("data", data)
if(data)
res.redirect(302, data.longUrl)
else
res.end()
})
})
app.post('/addUrl', function (req, res, next) {
console.log("on create");
Url.findOne({longUrl:req.body.longUrl}, function(err, data) {
if (err)
res.send(err);
else if(data) {
console.log("already exists",data)
res.send("http://localhost:3000/"+data.code);
} else {
var url = new Url({
code : Utility.randomString(6,"abcdefghijklm"),
longUrl : req.body.longUrl
});
console.log("in last else data created",url)
url.save(function (err, data) {
console.log(data)
if (err)
res.send(err);
else
res.send("http://localhost:3000/"+data.code);
});
}
});
})
app.listen(port, function () {
console.log('Example app listening on port 3000!')
});
// Connect to our mongo database
mongoose.connect('mongodb://localhost/shortUrl');
I get the Following error
error
_http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:718:10)
at ServerResponse.location (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:835:8)
at ServerResponse.redirect (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:874:8)
at Query.<anonymous> (/opt/lampp/htdocs/url-shortener/server/server.js:30:8)
at /opt/lampp/htdocs/url-shortener/node_modules/mongoose/node_modules/kareem/index.js:177:19
at /opt/lampp/htdocs/url-shortener/node_modules/mongoose/node_modules/kareem/index.js:109:16
at process._tickCallback (node.js:355:11)
From the execution order, in * route handler, the body is being assigned to the response and then in /:code, the response code 302 is being added, where Location header is also added, hence the error. Any header must be added before the body to the response.
To solve this problem, simply change the order of the two GET statements.
Finally found the solution:
var express = require('express')
var mongoose = require('mongoose')
var path = require('path')
var bodyParser = require("body-parser")
var app = express()
var port = process.env.PORT || 3000
var Url = require("./data/url-schema")
var Utility = require("./utility")
//Express request pipeline
app.use(express.static(path.join(__dirname,"../client")))
app.use(bodyParser.json())
/*
Your server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /dashboard your web server will get a request to /dashboard. You will need it to handle that URL and include your JavaScript application in the response.
*/
app.get('/dashboard', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/about', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/:code', function(req, res) {
Url.findOne({code:req.params.code}, function(err, data){
if(data){
res.redirect(302, data.longUrl)
}
})
})
app.post('/addUrl', function (req, res, next) {
Url.findOne({longUrl:req.body.longUrl}, function(err, data) {
if (err){
res.send(err)
}
else if(data) {
res.send("http://localhost:3000/"+data.code);
} else {
var newCode = getCode()
checkCode(newCode)
.then(function(data){
var url = new Url({
code : data,
longUrl : req.body.longUrl
});
url.save(function (err, data) {
if (err)
res.send(err);
else
res.send("http://localhost:3000/"+data.code);
});
})
}
});
})
app.listen(port, function () {
console.log('Example app listening on port 3000!')
});
// Connect to our mongo database
mongoose.connect('mongodb://localhost/shortUrl');
//Generate a random code
function getCode() {
return Utility.randomString(6,"abcdefghijklmnopqrstuvwxyz")
}
//Check if the code is unique
function checkCode(code) {
return new Promise(function (resolve, reject){
Url.findOne({code:code}, function(err, data) {
if(err === null){
resolve(code)
}else if(data){
saveUrlCode(getCode())
}
})
})
}
My earlier route which was :
app.get('*', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
The get route was getting executed twice on account of the above call and the
app.get(":/code") call.
So I had to handle the routes properly which I have done by handling the dashboard and about routes separately instead of using the "*" route.
I am trying to implement a simple API but get undefined is not a function when I navigate to api/users. Spent hours trying to figure this out, any suggestions would be greatly appreciated.
Stack trace
TypeError: undefined is not a function
at module.exports (//config/routes.js:10:9)
at Layer.handle [as handle_request] (/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/node_modules/express/lib/router/index.js:312:13)
at /U/node_modules/express/lib/router/index.js:280:7
at Function.process_params (//node_modules/express/lib/router/index.js:330:12)
at next (//node_modules/express/lib/router/index.js:271:10)
at logger (//node_modules/morgan/index.js:144:5)
at Layer.handle [as handle_request] (/U/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (//node_modules/express/lib/router/index.js:312:13)
at //node_modules/express/lib/router/index.js:280:7
at Function.process_params (//node_modules/express/lib/router/index.js:330:12)
at next (//node_modules/express/lib/router/index.js:271:10)
at SessionStrategy.strategy.pass (//node_modules/passport/lib/middleware/authenticate.js:318:9)
at SessionStrategy.authenticate (//node_modules/passport/lib/strategies/session.js:67:10)
at attempt (//node_modules/passport/lib/middleware/authenticate.js:341:16)
at authenticate (//node_modules/passport/lib/middleware/authenticate.js:342:7)
I have the following setup.
app.js
'use strict';
var express = require('express');
var env = 'development';
var app = express();
var config = require('./config/config')[env];
require('./config/mongoose')(config);
require('./config/express')(app, config);
require('./config/passport')();
require('./config/routes')(app);
app.listen(config.port, function() {
console.log('Express server listening on port ' + config.port);
console.log('env = ' + app.get('env') +
'\n__dirname = ' + __dirname +
'\nprocess.cwd = ' + process.cwd());
}
express.js
/* jshint -W117 */
var express = require('express'),
logger = require('morgan'),
bodyParser = require('body-parser'),
favicon = require('serve-favicon'),
cookieParser = require('cookie-parser'),
env = 'development',
session = require('express-session'),
passport = require('passport'),
four0four = require('../utils/404')();
module.exports = function(app, config) {
app.use(favicon(config.rootPath + '/favicon.ico'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(session({resave: false, saveUninitialized:false,
secret: 'all is well that ends well'}));
app.use(passport.initialize());
app.use(passport.session());
app.use(logger('dev'));
app.use('/api', require('./routes'));
console.log('About to crank up node');
console.log('PORT=' + config.port);
console.log('NODE_ENV=' + env);
switch (env){
case 'production':
console.log('** BUILD **');
app.use(express.static('./build/'));
// Any invalid calls for templateUrls are under app/* and should return 404
app.use('/app/*', function(req, res, next) {
four0four.send404(req, res);
});
// Any deep link calls should return index.html
app.use('/*', express.static('./build/index.html'));
break;
default:
console.log('** DEV **');
app.use(express.static('./src/client/'));
app.use(express.static('./'));
app.use(express.static('./tmp'));
// Any invalid calls for templateUrls are under app/* and should return 404
app.use('/app/*', function(req, res, next) {
four0four.send404(req, res);
});
// Any deep link calls should return index.html
app.use('/*', express.static('./src/client/index.html'));
break;
}
}
routes.js
var four0four = require('../utils/404')(),
auth = require('../config/auth'),
users = require('../controllers/users'),
mongoose = require('mongoose'),
User = mongoose.model('User');
module.exports = function(app) {
app.get('/users', auth.requiresRole('admin'), users.getUsers);
app.post('/users', users.createUser);
app.put('/users', users.updateUser);
app.post('/login', auth.authenticate);
app.post('/logout', function(req, res) {
req.logout();
res.end();
});
app.all('/*', four0four.notFoundMiddleware);
}
auth.js
var passport = require('passport');
exports.authenticate = function(req, res, next) {
req.body.username = req.body.username.toLowerCase();
var auth = passport.authenticate('local', function(err, user) {
if (err) {return next(err);}
if (!user) {res.send({success:false});}
req.login(user, function(err) {
if (err) {return next(err);}
res.send({success:true, user:user});
});
});
auth(req, res, next);
};
exports.requiresApiLogin = function(req, res, next) {
if (!req.isAuthenticated()) {
res.status(403);
} else {
next();
}
};
exports.requiresRole = function(role) {
return function(req, res, next) {
if (!req.isAuthenticated() || req.user.roles.indexOf(role) === -1) {
res.status(403);
res.end();
} else {
next();
}
};
}
I solution to this problem was a complete refactor of my routes.js file. I also removed this line from the app.js
require('./config/routes')(app);
Here is how the routes.js file looks after the refactor.
var four0four = require('../utils/404')(),
auth = require('../config/auth'),
users = require('../controllers/users'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
router = require('express').Router();
router.get('/users', auth.requiresRole('admin'), users.getUsers);
router.post('/users', users.createUser);
router.put('/users', users.updateUser);
router.post('/login', auth.authenticate);
router.post('/logout', logoutUser);
router.all('/*', four0four.notFoundMiddleware);
module.exports = router;
//////////////
function logoutUser (req, res){
req.logout();
res.end();
}