I have an Expressjs route which does a db INSERT (using Sequelize) based on some JSON Body params in the request. The bodyParser middleware does a JSON-schema validation on the body and returns an error if it doesn't validate.
The issue here is that something in bodyparser is executing asynchronously, and I'm getting errors such as null values being inserted into the DB (even after a failed validation), and Headers already returned to client errors.
How to best fix this?
The route:
var bodyParser = json_validator.with_schema('searchterm');
router.post('/', bodyParser, function (req, res, next) {
Searchterm.findOrCreate({
where: {searchstring: req.body.searchstring},
defaults: {funnystory: req.body.funnystory},
attributes: ['id', 'searchstring', 'funnystory']
}).spread((searchterm, created) => {
if (created) {
res.json(searchterm);
} else {
res.sendStatus(409);
}
}).catch(next);
});
The middleware:
var ajv = new Ajv({allErrors: true});
var jsonParser = bodyParser.json({type: '*/json'});
module.exports.with_schema = function(model_name) {
let schemafile = path.join(__dirname, '..', 'models', 'schemas', model_name + '.schema.yaml');
let rawdata = fs.readFileSync(schemafile);
let schema = yaml.safeLoad(rawdata);
var validate = ajv.compile(schema);
return function(req, res, next) {
jsonParser(req, res, next);
if (!validate(req.body)) {
res.status(400).send(JSON.stringify({"errors": validate.errors}));
}
}
};
Your middleware calls next too early; change:
return function(req, res, next) {
jsonParser(req, res, next);
if (!validate(req.body)) {
res.status(400).send(JSON.stringify({"errors": validate.errors}));
}
}
to:
return function(req, res, next) {
if (!validate(req.body)) {
res.status(400).send(JSON.stringify({"errors": validate.errors}));
}
}
and your route definition to:
router.post('/', jsonParser, bodyParser, function (req, res, next) { ... });
Related
I had originally been using bodyParser as so:
app.use(bodyParser.json());
However, now I want to conditionally use bodyParser:
app.use((req, res, next) => {
if (req.originalUrl === '/hooks') {
next();
} else {
bodyParser.json()(req, res, next);
}
});
When I try to remove (req, res, next), the parser does not work. That is,
app.use((req, res, next) => {
if (req.originalUrl === '/hooks') {
next();
} else {
bodyParser.json();
}
});
does not work.
Why do I need (req, res, next) after bodyParser.json()?
https://github.com/expressjs/body-parser/blob/master/index.js#L108
function bodyParser (options) {
var opts = {}
// exclude type option
if (options) {
for (var prop in options) {
if (prop !== 'type') {
opts[prop] = options[prop]
}
}
}
var _urlencoded = exports.urlencoded(opts)
var _json = exports.json(opts)
return function bodyParser (req, res, next) {
_json(req, res, function (err) {
if (err) return next(err)
_urlencoded(req, res, next)
})
}
}
Body parser is a middleware that needs access to res, req and next.
It parses your request using req and in order to pass control to the next middleware, it needs access to the next function.
Here app.use(bodyParser.json()); are passed (req, res, next) by default as
bodyParser.json() returns return function bodyParser (req, res, next) { .. }
so it becomes --> app.use(function bodyParser (req, res, next) { .. });
but in your case, you are creating a middleware by your self and you are responsible to pass the parameters to bodyParser so it can have access to the required arguments.
app.use((req, res, next) => {
if (req.originalUrl === '/hooks') {
next();
} else {
bodyParser.json()(req, res, next);
}
});
See how app.use works below
https://github.com/expressjs/express/blob/master/lib/application.js#L187-L242
I am writing my own app (both of back and frontend). I want to ask you guys if I am doing it in the right way.
I want to split server.js to a few files (in PHP I would use include() for it) but I am not sure if it is the right way.
Here is some code example:
const app = require('express')(),
fs = require('fs'),
http = require('http').Server(app),
io = require('socket.io')(https),
path = require('path'),
login_user = require('./routes/login_user'),
add_user = require('./routes/add_user'),
logout = require('./routes/logout');
app.post('/login_user', (req, res, next) => {
login_user.go(req, res, next);
});
app.post('/add_user', (req, res) => {
add_user.go(req, res);
});
app.get('/logout', (req, res) => {
logout.go(req, res);
});
Please note that's not the whole code and I want to focus on spliting "routes" or "paths" to a few files. For example a whole API login system is in /routes/login_user.js file (exported).
Now I have got so many paths and code is looking a little bit weird.
app.post('/check_balance', (req, res) => {
check_balance.go(req, res);
});
app.post('/add_incoming', (req, res) => {
add_incoming.go(req, res);
});
app.post('/add_outgoing', (req, res) => {
add_outgoing.go(req, res);
});
app.post('/add_category', (req, res) => {
add_category.go(req, res);
});
app.post('/change_settings', (req, res) => {
change_settings.go(req, res);
});
app.post('/login_user', (req, res, next) => {
login_user.go(req, res, next);
});
app.post('/add_user', (req, res) => {
add_user.go(req, res);
});
app.post('/verify_user', (req, res) => {
verify_user.go(req, res);
});
app.get('/logout', (req, res) => {
logout.go(req, res);
});
app.get('/check_settings', (req, res) => {
check_settings.go(req, res);
});
app.get('/check_categories', (req, res) => {
check_categories.go(req, res);
});
app.post('/remove_categories', (req, res) => {
remove_categories.go(req, res);
});
app.get('/check_incomings', (req, res) => {
check_incomings.go(req, res);
});
app.get('/check_outgoings', (req, res) => {
check_outgoings.go(req, res);
});
app.get('/check_both', (req, res) => {
check_both.go(req, res);
});
app.get('/check_outgoings_chart', (req, res) => {
check_outgoings_chart.go(req, res);
});
app.get('/check_incomings_chart', (req, res) => {
check_incomings_chart.go(req, res);
});
app.post('/remove_incomings', (req, res) => {
remove_incomings.go(req, res);
});
app.post('/remove_outgoings', (req, res) => {
remove_outgoings.go(req, res);
});
Make your server.js as simple as possible and extract all your routing logic to separate folder (possibly name it "routes"). If you also want to define yours schema, put it in separate folder ("models"). A complete solution can be like this:
in Model Folder:
user.js
const mongoose = require("mongoose"); // In case if you want to use MongoDB
const userSchema = new mongoose.Schema({
name: { type: String, required:true },
email: { type: String, required: true },
password: { type: String, required: true },
});
exports.User = User;
In routes folder:
users.js
const { User } = require("../models/user");
const router = express.Router();
//define your routes here
router.get('/', async(req,res)=>{
const users = await User.find();
res.send(users)
});
module.exports = router;
And finally in your server.js
const app = require('express')(),
fs = require('fs'),
http = require('http').Server(app),
io = require('socket.io')(https),
path = require('path'),
users = require('./routes/users');
app.use("/api/users", users); //With this API Endpoint you can access it like http://{your_domain}/api/users
If you want to make it more clean, you can wrap all routing paths to another folder. Lets call it "startup".
with this you can do like this.
in your startup folder:
routes.js
const users = require("../routes/users");
module.exports = function(app) {
app.use("/api/users", users);
//add all routes here
}
Then in your server.js
require("./startup/routes")(app); //all routes will be imported
I think this is what you need.
Let's say that there is a file called routers/login.js:
var express = require('express');
var router = express.Router();
router.get('/login', function(req, res) {
// do something
});
then app.js:
...
var login = require('./routes/login');
app.use("/login", login)
Put all the routes files in a folder with multiple files like User_routes.js can contain routes related to user and so on.
Also all you need to then is to export these routes from each file by using module.export.your_module and include them in your server file like in user routes :
// Login Page
router.get('/login', (req, res) => res.render('login'));
// Register Page
router.get('/register',(req, res) => {
res.render('register'); });
then import it as
module.exports = router;
also include it as :
app.use('/users', require('./routes/users.js'));
app.use('/',(req,res,next)=>{
console.log('I should handle it Now.');
res.render('404');
});
I am trying to pass some predefined functions in the callback of app.post() method. I am getting next is not defined error. Below is my code. Please suggest where I am doing wrong or am I missing any concept here?
var express = require('express');
var app = express()
app.post('/api/signup', function(req, res) {
validateParams(req, res, next),
dbCall(req, res, next),
sendResponse(req, res)
})
where I have each function defined and imported and returning next() after my process.
my validateParams function is below :
validateParams = function(req, res, next) {
console.log("at validator ", req);
next();
}
module.exports = validateParams;
my dbCall function is below :
dbCall = function(req, res, next) {
console.log("at dbCall ", req);
next();
}
module.exports = dbCall;
my sendResponse function is below :
sendResponse = function(req, res) {
console.log("at dbCall ", res);
res.send("Response sent successfully");
}
module.exports = sendResponse;
You probably forgot to add the next argument in your callback.
app.post('/api/signup', function(req, res, next) {
validateParams(req, res, next),
dbCall(req, res, next),
sendResponse(req, res)
})
I think you are trying to use validateParams(req, res, next) and dbCall(req, res, next) as middleware functions. In this case, you need something like this:
const validateParams = (req, res, next) => {
// do stuff here
next();
}
const dbCall = (req, res, next) => {
// do stuff here
next();
}
app.post('/api/signup', validateParams, dbCall, function(req, res) {
sendResponse(req, res)
})
You can read more here
I'm using:
express 4.14
node 7.0+
session 1.14+
I created Nodejs project with webstorm:
app.js
var express = require('express');
var session = require('express-session');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var index = require('./routes/index');
var user = require('./routes/user');
var app = express();
var check = function() {
!req.session.status ? res.redirect('/user/login') : next();
}
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({secret: "inline", resave: false, saveUninitialized: true, status: false}));
app.use('/', check, require('./routes/index'));
app.use('/user', require('./routes/user'));
module.exports = app;
index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
user.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/login', function(req, res, next) {
res.render('login');
});
module.exports = router;
When I request to localhost:3000, the page prompt me that "too many redirects', now the url is localhost:3000/user/login?
What's my problem?
fix:
var check = function(req, res, next) {
!req.session.status ? res.redirect('/user/login') : next();
}
But I'll write more beautiful solution for You, hope You'll get in idea:
app.js:
app.use(require('./routes'));
routes folder:
routes/
|- index.js
|- common/
|- auth.js
|- root/
|- index.js
|- posts.js
|- backend/
|- index.js
|- posts.js
...
1) routes/index.js :
const
express = require('express'),
router = express.Router();
// guarded routes
function guardFn(req, res, next) {
let authenticated = req.session && req.session.authenticated === true;
authenticated ? next() : res.redirect('/auth');
}
router.use('/backend', guardFn, require('./backend'));
// public routes
router.use('/auth', require('./common/auth'); // for auth purposes
router.use('/', require('./root')); // for routes that starts from /, try not to rewrite /backend, /auth
module.exports = router;
2) routes/common/auth :
const
express = require('express'),
router = express.Router();
mongoose = require('mongoose'),
User = mongoose.model('user');
router.get('/', (req, res) => {
res.render('common/auth');
});
router.post('/', (req, res) => {
User
.findOne({
username: req.body.username,
password: req.body.password
})
.exec((err, user) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
if(!user) return res.redirect('back');
req.session.user = user._id;
req.session.authenticated = true;
res.redirect('/backend');
});
});
function logout(req, res, next) {
delete req.session.user;
req.session.authenticated = false;
next();
}
router.delete('/auth', logout, (req, res) => res.send({success: true}));
router.get('/auth/destroy', logout, res => res.redirect('/auth'));
module.exports = router;
3) routes/root/index.js :
const
express = require('express'),
router = express.Router();
router.get('/', (req, res) => {
res.render('site/welcome');
});
module.exports = router;
4) routes/root/posts.js :
const
express = require('express'),
router = express.Router(),
mongoose = require('mongoose'),
Post = mongoose.model('post');
router.get('/', (req, res) => {
Post
.find()
.skip((req.query.page-1)*10)
.limit(10)
.exec((err, posts) => {
res.render('site/posts/list', {posts});
});
});
router.get('/:id', (req, res) => {
Post
.findById(req.params.id)
.exec((err, post) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
res.render('site/posts/show', {post});
});
});
module.exports = router;
5) routes/backend/index.js :
const
express = require('express'),
router = express.Router();
router.get('/', (req, res) => {
res.render('backend/dashboard');
});
module.exports = router;
6) routes/backend/posts.js :
const
_ = require('lodash'),
express = require('express'),
router = express.Router(),
mongoose = require('mongoose'),
Post = mongoose.model('post');
router.get('/', (req, res) => {
Post
.find()
.skip((req.query.page-1)*50)
.limit(50)
.exec((err, posts) => {
res.render('backend/posts/list', {posts});
});
});
router.get('/:id', (req, res) => {
Post
.findById(req.params.id)
.exec((err, post) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
res.render('backend/posts/show', {post});
});
});
function updatePost(id, data, callback) {
Post
.findById(_id)
.exec((err, post) => {
if(err) return callback(err);
if(!post) return callback('not found');
post = _.extend(post, data);
post.save(() => callback(null, post));
});
}
router.put('/:id', (req, res) => {
updatePost(req.params.id, req.body, (err, post) => {
if(err) return res.status(500).send({success: false, err});
res.send({success: true, post});
});
});
router.post('/:id', (req, res) => {
updatePost(req.params.id, req.body, (err, post) => {
if(err) return res.status(500).send(err);
res.redirect('/backend/posts');
});
});
function createPost(data, callback) {
let post = new Post();
post = _.extend(post, req.body);
post.save((err) => callback(err, post));
}
router.post('/', (req, res) => {
createPost(req.body, (err, post) => {
if(req.xhr) {
if(err) return res.status(500).send({success: false, err});
return res.send({success: true, post});
}
if(err) return res.status(500).send(err);
res.redirect('/backend/posts');
});
});
module.exports = router;
As I understand it, app.use('/', ...) will match all requests which is why your check method is being called for all requests.
More good info here.
From Express docs:
A route will match any path that follows its path immediately with a
“/”.
For example: app.use("/apple", ...) will match “/apple”,
“/apple/images”, “/apple/images/news”, and so on.
I would suggest refactoring to:
var check = function(req, res, next) {
if (!req.session.status && req.path != '/user/login') {
res.redirect('/user/login');
}
else {
next();
}
}
app.use(check);
app.use('/', index);
app.use('/user', users);
So check will execute for all requests but only redirect if the request session is falsey and request path doesn't match /user/login
Keep in mind your code will fail if req.session is not set...
I have defined multiple route middleware and want to share them across multiple routes/controllers.
Here is my setup:
app.js requires ./routes/index.js:
// load fs module
var fs = require('fs');
// import routing files
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
};
index.js loads all routes automaticly in the dir. A possible routes file can look like:
module.exports = function(app) {
app.get('/contacts', function(req, res, next) {
// routing stuff
});
};
Now I got route middleware:
function isAuthenticated(req, res, next) {
if (!req.session.authenticated) return next(new Error('user not authenticated'));
};
function loadUser(req, res, next) {
var query = User.findById(req.session.user_id);
query.populate('contacts');
query.exec(function(err, user) {
if (err) return next(err);
req.user = user;
next();
});
}
which I want to use like:
var User = require('../models/user');
module.exports = function(app) {
app.get('/contacts', isAuthenticated, loadUser, function(req, res, next) {
res.json(req.user.contacts);
});
};
I also would like to avoid requiring them accross all routing files.
A possible solution would also be:
// load fs module
var fs = require('fs');
var routeMiddleware = {
loadUser: function(req, res, next) { // logic },
isAuthenticated: function(req, res, next) { // logic },
};
// import routing files
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app, routeMiddleware);
});
};
but I think not the best...
Personally I would declare shared middleware in the app, not in the controllers, i.e.:
routes/home.js:
module.exports = function(req, res, next) { \\ Code }
app.js:
app.get('/', thisMiddleware, thatMiddleware, require('./routes/home'))
You can also make a stack (array, not an object):
theseMiddlewares = [thisMiddleware, thatMiddleware]
app.get('/', theseMiddlewares, require('./routes/home'))
And if these middlewares are used on all routes except a few, you can do the following:
theseMiddlewares = function(req, res, next) {
if (req.url.match(some_regex_for_urls_to_skip)) next()
else {
\\ do stuff...
next()
}
}
Now you can app.use(theseMiddlewares) that middleware, or if it needs to happen in a certain order relative to other middleware, you can use app.all('*', theseMiddlewares)