Cannot Post in Node.js Express - node.js

Following code is giving me the error
Cannot POST /campgrounds/5b0d6eb8a0f5990b452e8212/comments
When I submit my form from new.js i get an error.
I have one more Post Route and it works fine which means that my body-parser has no issue.
The aim here is when the user submit the comment, he should be navigated back to show.ejs where the camps are shown as well as the new comment
I am running this app on Cloud9.
App.js
var express = require("express");
var app = express();
var bodyParser = require("body-parser");
var mongoose = require("mongoose");
var Campground = require("./models/campground");
var Comment = require("./models/comment");
var seedDB = require("./seeds.js");
mongoose.connect("mongodb://localhost/yelp_camp");
app.use(bodyParser.urlencoded(
{
limit: "10mb",
extended:true
}));
app.set("view engine", "ejs");
seedDB();
app.get("/",function(req,res){
res.render("landing");
});
app.get("/campgrounds",function(req,res){
// Get all Campround from db
Campground.find({},function(err,allCampgrounds){
if(err){
console.log(err);
} else{
res.render("campgrounds/index",{campgrounds:allCampgrounds});
}
});
//
});
app.post("/campgrounds", function(req,res){
//res.send("Hitting the Post ROute");
var name = req.body.name;
var image = req.body.image;
var description = req.body.description;
var newCampground = {name: name, image: image,description : description};
// New Campground and Save to DB..
Campground.create(newCampground,function(err,newlyCreated){
if(err){
console.log(err);
} else{
res.redirect("/campgrounds");
}
});
// campgrounds.push(newCampground);
//get data from form and add them to array...
});
app.get("/campgrounds/new",function(req, res) {
res.render("campgrounds/new");
});
//Show More Info on Camps
app.get("/campgrounds/:id",function(req, res) {
// Find Campground using Id
Campground.findById(req.params.id).populate("comments").exec(function(err,foundCamp){
if(err){
console.log(err);
}else{
//render show page
console.log(foundCamp);
res.render("campgrounds/show",{campground:foundCamp});
}
});
// req.params.id
});
/*
====================
Comments
+===================*/
app.get("/campgrounds/:id/comments/new", function(req, res) {
Campground.findById(req.params.id,function(err,campground){
if(err){
console.log(err);
}else{
res.render("comments/new", {campground:campground});
}
});
});
app.post("campgrounds/:id/comments",function(req,res){
//luk for campground
Campground.findById(req.params.id, function(err,campground){
if(err){
console.log(err);
res.redirect("/campgrounds");
} else {
Comment.create(req.body.comment, function(err, comment){
if(err){
console.log(err);
}else{
// var text = req.body.text;
// var author = req.body.text;
// console.log(text);
campground.comments.push(comment);
campground.save();
//console.log(comment);
res.redirect('/campgrounds/' + campground._id);
}
});
}
});
//create new cpmment
//comment new comment to
//redirect
});
app.listen(process.env.PORT,process.env.IP, function(){
console.log("Yelp Server Started...")
});
New.js
<% include ../partials/header %>
<div class="container">
<h1 style="text-align:center;">Add New Comment to <%= campground.name %></h1>
<div class="row">
<div style="width:30%;margin:0 auto;">
<form action="/campgrounds/<%= campground._id %>/comments" method="POST">
<div class="form-group">
<input class="form-control" type="text" name="comment[text]" placeholder="Text">
</div>
<div class="form-group">
<input class="form-control" type="text" name="comment[author]" placeholder="Author">
</div>
<button class="btn btn-lg btn-primary btn-block">Submit</button>
</form>
</div>
</div>
</div>
<% include ../partials/footer %>
Show.ejs
<% include ../partials/header %>
<h1><%= campground.name %></h1>
<p></p>
<img src="<%= campground.image %>"/>
<p><%= campground.description %></p>
<p>
<a class="btn btn-success" href="/campgrounds/<%= campground._id %>/comments/new">Comment</a>
</p>
<% campground.comments.forEach(function(comment){ %>
<p><strong><%= comment.author %></strong> - <%= comment.text %> </p>
<% }); %>
<% include ../partials/footer %>

Please add a forward-slash character to the start of your route path parameter.
Currently you have "campgrounds/:id/comments"
You are expecting a POST request on "/campgrounds/:id/comments"

Related

req flash not showing messages with passport js

using the express-flash package together with passportjs, and I want to flash messages to a user.
App.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const sessions = require('express-session');
const passport = require('passport');
const passportInit = require('./config/passport');
const sessionStoreSQL = require('express-mysql-session')(sessions);
const logger = require('morgan');
const flash = require('express-flash');
const favicon = require('serve-favicon');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser('keyboard cat'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(sessions({
genid: (req) => {
return uuid.v1();
},
secret:'-----',
resave:false,
saveUninitialized:false,
store:sessionStore
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passportInit(passport,userModel);
require('./routes/index')(app,passport);
require('./server/API/get')(app);
I use a custom middle ware function to map my errors to, so I can access all of them in my templates
app.get('*', function(req,res,next){
res.locals.successes = req.flash('success');
res.locals.errors = req.flash('error');
res.locals.warnings = req.flash('warning');
next();
});
Passport.js
passport.use('local-login', new localStategy({passReqToCallback:true},function (req,username,password,done){
const isValidPassword = (userpass,password) => {
return bcrypt.compareSync(password,userpass);
}
Model.findOne({
where:{
'username':username,
},
}).then(function(user){
if(!user) return done(null,false,req.flash('warning','User does not exist'));
if(!isValidPassword(user.password,password)) return done(null,false,req.flash('error','Incorrect password'));
return done(null, user);
}).catch(err => console.log(err));
}))
Here is where I flash messages to the user.
Then I have a EJS component that handles al my alerts
Alerts.ejs
<% if (errors.lenght > 0) { %>
<div class='header alert alert-danger alert-dismissible'>
<strong><i class="fa fa-exclamation-circle"></i> ERROR:</strong> <%- errors.message %>
<i class='fa fa-times'></i>
</div>
<% } %>
<% if (successes.lenght > 0 ) { %>
<div class='header alert alert-success alert-dismissible'>
<strong><i class="fa fa-check-circle"></i> Success!</strong> <%- successes.message %>
<i class='fa fa-times'></i>
</div>
<% } %>
<% if (warnings.lenght > 0) { %>
<div class='header alert alert-warning alert-dismissible'>
<strong><i class="fa fa-check-circle"></i> Warning:</strong> <%- warnings.message %>
<i class='fa fa-times'></i>
</div>
<% } %>
This is then included in my templates e.g login and register like so
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %> </title>
<% include components/header.ejs %>
</head>
<body>
<% include components/navbar.ejs %>
<div class="container-fluid">
<% include components/alerts.ejs %>
<div class="row justify-content-center">
<div class="col-auto">
<form method="POST" action="/login">
<div class="form-group">
<label for="login-username">Username</label>
<input name="username" type="text" class="form-control" id="loginUsername"
aria-describedby="emailHelp" placeholder="Enter Username">
</div>
<div class="form-group">
<label for="login-password">Password</label>
<input name="password" type="password" class="form-control" id="loginPassword"
placeholder="Password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</body>
<% include components/scripts.ejs %>
</html>
Routes.js
/* eslint-disable no-unused-vars */
module.exports = function (app,passport) {
app.get('/', async function (req, res) {
res.render('index', {title:'Home'});
});
app.post('/',function (req,res) {
})
app.get('/cryptofolio/:username',isAuthenticated, function(req,res) {
res.render('cryptofolio', {title:'Cryptofolio'});
})
app.post('/portfolio',function(req,res){
})
app.get('/login',function(req,res){
res.render('login',{title:'Login'});
});
app.post('/login',passport.authenticate('local-login',{successRedirect: '/',failureRedirect:'/login',failureFlash:true}));
app.get('/register',function (req,res){
res.render('register',{title:'Register'});
})
app.post('/register',passport.authenticate('local-register',{successRedirect: '/',failureRedirect:'/register',failureFlash:true}));
app.get('/logout',function (req,res) {
req.logout();
res.redirect('/');
})
function isAuthenticated(req,res,next){
if (req.isAuthenticated()){
return next();
}
res.redirect('/login');
}
};
But when I input wrong information no errors are being flashed like they should.D
i think the check inside the template is returning false, as it needs to be checking for length not lenght

user.findOne is not a function

I am learning to use passport js in my form application and I am using a user object in an application that uses this model to save a user in database but when I return a user object that has been created and try to update that object by using findOne method I run into user.findOne is not a function error Here is my code.
Thanks in advance for helping me out.
user.js looks something like this
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var usersSchema = new mongoose.Schema({
username:String,
password:String,
email:String
});
usersSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User",usersSchema);
my signup page looks like this
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css2?family=Mitr:wght#500&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="css/biodata.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand justify-content-around" id="navbar-home" href="/">Home</a>
<div class="ml-auto" id="navbar-joint">
<a class="navbar-brand" href="/signup">Sign Up</a>
<a class="navbar-brand" href="/logout">Logout</a>
</div>
</nav>
<div class="card" id="signup-card">
<div class="card-body" id="signup-card-body">
<form action="/register" method="POST">
<h3 class="d-flex justify-content-center">SIGN UP</h3>
<div class="form-group">
<input type="text" class="form-control form-control-lg" id="username" name="username" placeholder="Username">
</div>
<div class="form-group">
<input type="email" class="form-control" id="email" name="email" placeholder="Email">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-lg" id="inputPassword" name="inputPassword" placeholder="Password">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-lg" id="confirminputPassword" name="confirminputPassword" placeholder="Confirm Password">
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1" name="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">I agree</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg btn-block">Submit</button>
</div>
</form>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
my app.js looks like this
var express = require("express");
var app = express();
var mongoose = require("mongoose");
var passport = require("passport"),
localStrategy = require("passport-local");
const User = require("./models/User");
var expressSession = require("express-session");
mongoose.connect('mongodb://localhost/users', {useNewUrlParser: true, useUnifiedTopology: true}).catch(function(reason){
console.log('Unable to connect to the mongodb instance. Error: ', reason);
});
app.set('view engine', 'ejs');
var bod = require("body-parser");
app.use(bod.urlencoded({extended:true}));
app.use(passport.initialize());
app.use(passport.session());
app.use(expressSession({
secret:"the key",
resave:false,
saveUninitialized:false
}));
passport.use(new localStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(express.static(__dirname + '/resources'));
/*
var usersSchema = new mongoose.Schema({
username:String,
password:String,
email:String
});
var user = mongoose.model("User",usersSchema);
*/
app.get("/", function(req, res) {
res.render("home");
});
app.get("/home", function(req, res) {
res.render("home");
});
//problem method
app.post("/register",function(req,res){
var userName = req.body.username;
var inputPassword = req.body.inputPassword;
var email = req.body.email;
var exampleCheck1 = req.body.exampleCheck1;
var confirminputPassword = req.body.confirminputPassword;
if(exampleCheck1==="on"){
if(inputPassword===confirminputPassword){
User.register( new User({username:userName}),inputPassword, function(err,user){
console.log(user);
// here I tried to cast the returned object into User.js object but it didn't work
// user = new User();
if(err){
console.log(err);
return res.render("signup");
}
//I am trying to update returned object with email, I am not sure if this is the correct way to do it, would welcome suggestions. but at this point the user.findOne is not a function error is thrown.
user.findOne({username:userName},function(err,user){
user.email = email;
user.save();
console.log(user);
});
passport.authenticate("local")(req,res,function(){
res.render("bioform",{userName:userName});
});
});
}else{
console.log("Passwords doesn't match!!!");
res.render("signup");
}
}else{
console.log("Agree to Terms!!!");
res.render("signup");
}
});
//
app.get("/signup", function(req, res) {
res.render("signup");
});
app.listen(3000,function(){
console.log("Server started");
});
You are trying to call findOne in the object returned by the call to register from passport-local-moongoose.
Instead, you should call it through the User model:
const User = require("./models/User");
[...]
User.register(new User({ username: userName }), inputPassword, function(err, user) {
[...]
// Use User model instead of the object returned in User.register.
User.findOne({ username: userName }, function(err, user) {
// Your logic here.
});
});

How do I combine two GET requests/databases into 1?

I want to combine a weather API with a form where you fill in your mood into 1 app:
a form where you fill in you city (so you get the weather) and where you fill in your mood of that day.
In my server.js file I have two GET and two POST functions.
However, I think I can only use res.render('index',...) once.
How to combine the two GET functions into 1 working function?
server.js file
const express = require('express')
const app = express()
app.use(express.static('./public'))
// --> npm install body-parser
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// --> npm install ejs
app.set('view engine', 'ejs');
// --> npm install request
// --> npm install request-promise
const request = require('request-promise');
// --> npm install mongoose
const mongoose = require('mongoose');
mongoose.connect('mongodb+srv://MONGO_HOST/test?retryWrites=true&w=majority');
// apiKey
const apiKey = 'XXX';
// database weather
var citySchema = new mongoose.Schema({
city: String
});
var cityModel = mongoose.model('City', citySchema);
// database mood
var moodSchema = new mongoose.Schema({
name: String,
description: String,
date: String
});
var moodModel = mongoose.model('Mood', moodSchema);
// function which gets a list of moods in the database
async function getDiary(moods) {
var diary_data = [];
for (var mood_obj of moods) {
var name = mood_obj.name;
var description = mood_obj.description;
var date = mood_obj.date;
var diary = {
name : name, // variable declared above
description : description, // variable declared above
date : date // variable declared above
};
diary_data.push(diary);
}
return diary_data;
};
// GET route mood
app.get('/', function (req, res) {
moodModel.find({}, function(err, moods){
console.log(moods);
getDiary(moods).then(function(results){
var diary_data = {diary_data: results};
res.render('index', diary_data);
})
});
})
// function which gets a list of cities in the database
async function getWeather(cities) {
var weather_data = [];
for (var city_obj of cities) {
var city = city_obj.name;
var url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperial&appid=${apiKey}`;
var response_body = await request(url);
var weather_json = JSON.parse(response_body);
var weather = {
city : city, // variable declared in this document
temperature: weather_json.main.temp, // variable from API data
description : weather_json.weather[0].description, // variable from API data
icon : weather_json.weather[0].icon, // variable from API data
};
weather_data.push(weather);
}
return weather_data;
};
// GET route weather
app.get('/', function (req, res) {
cityModel.find({}, function(err, cities){
console.log(cities);
getWeather(cities).then(function(results){
var weather_data = {weather_data: results};
res.render('index', weather_data);
})
});
})
// POST route mood
app.post('/', function(req, res) {
var newMood = new moodModel({
name: req.body.mood_name,
description: req.body.mood_description,
date: req.body.mood_date
});
newMood.save();
res.redirect('/');
})
// POST route weather
app.post('/', function(req, res) {
var newCity = new cityModel({city: req.body.city_name});
newCity.save();
res.redirect('/');
});
// setup port
const port = 3000;
app.listen(port, function() {
console.log(`server is listening on port ${port}`);
})
index.ejs file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet' type='text/css'>
</head>
<body>
<div class="container">
<% for (diary of diary_data) { %>
<div class="moodBox">
<span class="title"><%= diary.name %></span>
<br>
<span class="title"><%= diary.description %></span>
<br>
</div>
<% } %>
<% for (weather of weather_data) { %>
<div class="weatherBox">
<span class="title"><%= weather.city %></span>
<br>
<span class="subtitle"><%= weather.temperature %></span>
<br> <%= weather.description %>
</div>
<% } %>
<fieldset>
<form method="post" action="/">
<div>
<label for="mood_name">mood name</label>
<input type="text" name="mood_name" required>
</div>
<div>
<label for="mood_description">mood description</label>
<input type="text" name="mood_description" required>
</div>
<div>
<input type="date" name="mood_date">
<div id="theDate"></div>
</div>
<div>
<label for="city_name">City name</label>
<input type="text" name="city_name">
</div>
<button type="submit">Submit</button>
</form>
</fieldset>
</div>
<script src="/extra.js"></script>
</body>
</html>

nodejs: express-validator errors undefined in ejs template

I am using ejs engine instead of pug. When I click register button, I got errors undefined in my register view. There was only little chance that I can get the validation messages, but when I click other links and then back to the register page, the same error occurred again.
Here's my code:
app.js
//app.js code
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var passport = require('passport');
var expressValidator = require('express-validator');
var LocalStrategy = require('passport-local').Strategy;
var multer = require('multer');
var upload = multer({dest: './uploads'});
var flash = require('connect-flash');
var mongo = require('mongodb');
var mongoose = require('mongoose');
var db = mongoose.connection;
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Handle Sessions
app.use(session({
secret:'secret',
saveUninitialized: true,
resave: true
}));
// Passport
app.use(passport.initialize());
app.use(passport.session());
// Validator
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.')
, root = namespace.shift()
, formParam = root;
while(namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param : formParam,
msg : msg,
value : value
};
}
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(flash());
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
app.use('/', routes);
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
user.js
//user.js code
var express = require('express');
var router = express.Router();
var multer = require('multer');
var upload = multer({dest: 'uploads/'});
/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('members', {page_name : 'members'});
});
router.get('/register', function(req, res, next) {
res.render('register', { page_name: 'register' });
});
router.post('/register', upload.single('profileimage'), function(req, res) {
var name = req.body.name;
var username = req.body.username;
var email = req.body.email;
var password = req.body.password;
var password2 = req.body.password2;
if(req.file){
console.log("uploading file");
var profileimage = req.file.filename;
} else{
var profileimage = "noimage.jpg";
}
req.checkBody('name','Name field is required').notEmpty();
req.checkBody('email','Email field is required').notEmpty();
req.checkBody('email','Email is not valid').isEmail();
req.checkBody('username','Username field is required').notEmpty();
req.checkBody('password','Password field is required').notEmpty();
req.checkBody('password2','Passwords do not match').equals(req.body.password);
// Check Errors
errors = req.validationErrors();
//var errors = JSON.stringify(req.validationErrors());
if(errors){
console.log("errors: " + errors);
res.render('register', {errors: errors});
} else{
console.log('No Errors');
res.render("/");
}
});
router.get('/login', function(req, res, next) {
res.render('login', { page_name: 'login' });
});
module.exports = router;
register.ejs
//register.ejs code
<%include layout%>
<div class="container">
<% if(errors){errors.forEach(function(error){%>
<div class="alert alert-danger"><%= error.msg %></div>
<% })} %>
<h4>register</h4>
<form action="/users/register" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleFormControlInput1">Name</label>
<input type="text" class="form-control" name="name" placeholder="John">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Email address</label>
<input type="email" class="form-control" name="email" placeholder="name#example.com">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Username</label>
<input type="text" class="form-control" name="username" placeholder="username">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Password</label>
<input type="password" class="form-control" name="password" placeholder="password">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Confirm Password</label>
<input type="password" class="form-control" name="password2" placeholder="confirm password">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Profile Image</label>
<input type="file" class="form-control" name="profileimage" >
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
Error
ReferenceError: /Users/duanzhen/Documents/web_workspace/12_projects/node_auth/views/register.ejs:5
3| <div class="container">
4|
>> 5| <% if(errors){errors.forEach(function(error){%>
6|
7| <div class="alert alert-danger"><%= error.msg %></div>
8|
errors is not defined
at eval (eval at compile (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/ejs/lib/ejs.js:549:12), <anonymous>:22:8)
at returnedFn (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/ejs/lib/ejs.js:580:17)
at tryHandleCache (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/ejs/lib/ejs.js:223:34)
at View.exports.renderFile [as engine] (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/ejs/lib/ejs.js:437:10)
at View.render (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/view.js:127:8)
at tryRender (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/application.js:640:10)
at Function.render (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/response.js:971:7)
at /Users/duanzhen/Documents/web_workspace/12_projects/node_auth/routes/users.js:12:9
at Layer.handle [as handle_request] (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/layer.js:95:5)
at /Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/index.js:335:12)
at next (/Users/duanzhen/Documents/web_workspace/12_projects/node_auth/node_modules/express/lib/router/index.js:275:10)
That's because you are trying to access a non existing variable, note that errors variable is only generated and returned to the view if there where validation errors in your form, otherwise it's undefined, that's why in your condition you have to check if errors variable exists, Like this:
if (typeof errors !== 'undefined') { ...
Note: The typeof operator returns a string: the type of the variable, if the variable is not declared it will return undefined
npm uninstall express-validator --save
npm install express-validator#2.20.8 --save
Have a look at my code, it is working
in file 'index.js'
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Home' ,
success:false,
errors:req.session.errors ,
success:req.session.success });
req.session.errors=null;
});
//////////////checking the form validation
router.post('/submit', function(req, res, next) {
req.check('email','Invalid Email Address!!!!!').isEmail();// it's an built in exprexx validator, but we can also write our own
req.check('password','Pssword lenght must be greater than 5!! ').isLength({min:5});
req.check('password','Password is not confirmed!!').equals(req.body.confirmpassword);
var errors=req.validationErrors();
if (errors){
req.session.errors=errors;
req.session.success=false;
}
else{
req.session.success=true;
}
res.redirect('/');
});
module.exports = router;
in file 'index.ejs'
<h1>Fill the form below..</h1><h1></h1>
<h1>It's an example of Express Validator..</h1><h1></h1>
<% if (success) { %>
<h1><span class="badge badge-success">Congrats!! Form validation is secceded!!!</span></h1>
<% } else { %>
<% if (errors) { %>
<div class="alert alert-danger" role="alert">
<h1><span class="badge badge-danger">Errors Occured!! </span></h1>
<% errors.forEach(function(errors) { %>
<h5><%= errors.msg %> </h5>
<% }); %>
</div>
<% } else { %>
<form action="/submit" method="POST">
<div class="form-row">
<div class="col-7">
<input type="email" class="form-control" id="email" placeholder="email" name="email">
</div>
<div class="col">
<input type="password" class="form-control" id="password" placeholder="password" name="password">
</div>
<div class="col">
<input type="password" class="form-control" id="confirmpassword" placeholder="confirmpassword" name="confirmpassword">
</div>
</div>
<h1></h1>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<% } %>
<% } %>
And of course don't forget to add this lines in 'app.js' file:
//adding validator and session
app.use(expressValidator());
app.use(expressSession({secret:'max',saveUninitialized:false,resave:false}));
<% if(locals.errors){locals.errors.forEach(function(error){%>
<div class="alert alert-danger"><%= error.msg %></div>
<% })} %>

Express4 and Formidable file upload works until I enable csrf

I am working through Ethan Brown's book "Web Development with Node and Express" and it has been going well until I got to enabling csrf the multipart/form-data upload on the photo upload. I downloaded the full book code from Github, https://github.com/EthanRBrown/web-development-with-node-and-express and that does the same thing, works until csrf is enabled then it errors with:
Error: invalid csrf token
here are the bits of code I think are relevant, /meadowlark.js starting at line 100
app.use(require('cookie-parser')(credentials.cookieSecret));
app.use(require('express-session')({ store: sessionStore,
secret: credentials.cookieSecret,
name: credentials.cookieName,
saveUninitialized: true,
resave: true }));
app.use(express.static(__dirname + '/public'));
app.use(require('body-parser')());
// cross-site request forgery protection
app.use(require('csurf')());
app.use(function(req, res, next){
res.locals._csrfToken = req.csrfToken();
next();
});
// database configuration
var mongoose = require('mongoose');
var options = {
server: {
socketOptions: { keepAlive: 1 }
}
};
Then in /handlers/contest.js
var path = require('path'),
fs = require('fs'),
formidable = require('formidable');
// make sure data directory exists
var dataDir = path.normalize(path.join(__dirname, '..', 'data'));
var vacationPhotoDir = path.join(dataDir, 'vacation-photo');
fs.existsSync(dataDir) || fs.mkdirSync(dataDir);
fs.existsSync(vacationPhotoDir) || fs.mkdirSync(vacationPhotoDir);
exports.vacationPhoto = function(req, res){
var now = new Date();
res.render('contest/vacation-photo', { year: now.getFullYear(), month: now.getMonth() });
};
function saveContestEntry(contestName, email, year, month, photoPath){
// TODO...this will come later
}
exports.vacationPhotoProcessPost = function(req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files){
if(err) return res.redirect(303, '/error');
if(err) {
res.session.flash = {
type: 'danger',
intro: 'Oops!',
message: 'There was an error processing your submission. ' +
'Pelase try again.',
};
return res.redirect(303, '/contest/vacation-photo');
}
var photo = files.photo;
var dir = vacationPhotoDir + '/' + Date.now();
var path = dir + '/' + photo.name;
fs.mkdirSync(dir);
fs.renameSync(photo.path, dir + '/' + photo.name);
saveContestEntry('vacation-photo', fields.email,
req.params.year, req.params.month, path);
req.session.flash = {
type: 'success',
intro: 'Good luck!',
message: 'You have been entered into the contest.',
};
return res.redirect(303, '/contest/vacation-photo/entries');
});
};
exports.vacationPhotoEntries = function(req, res){
res.render('contest/vacation-photo/entries');
};
and the views/contest/vacation-photo.handlebars
<form class="form-horizontal" role="form"
enctype="multipart/form-data" method="POST"
action="/contest/vacation-photo/{{year}}/{{month}}">
<input type="hidden" name="_csrf" value="{{_csrfToken}}">
<div class="form-group">
<label for="fieldName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-4">
<input type="text" class="form-control"
id="fieldName" name="name">
</div>
</div>
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input type="email" class="form-control" required
id="fieldName" name="email">
</div>
</div>
<div class="form-group">
<label for="fieldPhoto" class="col-sm-2 control-label">Vacation photo</label>
<div class="col-sm-4">
<input type="file" class="form-control" required accept="image/*"
id="fieldPhoto" data-url="/upload" name="photo">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
What is the proper way to make csrf work?
Thanks,
On vacation-photo GET request, you should send csrf token like below.
exports.vacationPhotoEntries = function(req, res){
res.render('contest/vacation-photo/entries', { _csrfToken: req.csrfToken()});
};
You can also catch csrf token error in your default error handler like below.
// error handler
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
// handle CSRF token errors here
res.status(403)
res.send('session has expired or form tampered with')
})
For more info, please check this link.
Append csrf token as query string to action Url..
It works!
<form class="form-horizontal" role="form" enctype="multipart/form-data" method="POST"
action="/contest/vacation-photo/{{year}}/{{month}}?_csrf={{_csrfToken}}">
</form>

Resources