Form validation with flash messages in express js - node.js

I am trying to validate a form in express js before i post it onto the mongodb which i am using as a backend. My user.js for registration page looks something like this -
router.post('/register', (req, res) => {
userreg.register(
// eslint-disable-next-line new-cap
new userreg({
firstname: req.body.firstname,
lastname: req.body.lastname,
username: req.body.email,
usn: req.body.usn, // validate so that no space is taken or else request modal wont work
course: req.body.course,
}),
req.body.password,
(err) => {
if (err) {
console.log(err);
res.render('register', { user: 'error' });
} else {
console.log('no error');
res.render('submit-success', { username: req.body.firstname });
}
}
);
});
and my register.ejs looks something like this -
<form class="user" action = '/users/register' method="POST">
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text" class="form-control form-control-user" id="exampleFirstName" name="firstname" placeholder="First Name">
</div>
<div class="col-sm-6">
<input type="text" class="form-control form-control-user" id="exampleLastName" name="lastname" placeholder="Last Name">
</div>
</div>
<div class="form-group">
<input type="email" class="form-control form-control-user" id="exampleInputEmail" name="email" placeholder="Email Address">
</div>
<div class="form-group">
<input type="text" class="form-control form-control-user" id="exampleInputUSN" name="usn" placeholder="USN">
</div>
<div class="form-group">
<select class=" form-control selectpicker" name="course">
<optgroup label="Course"></optgroup>
<option selected hidden disabled>Course</option>
<option value="mca">Computer Applications</option>
<option value="mba">Business Administration</option>
</optgroup>
</select>
</div>
<div class="form-group">
<input type="password" class="form-control form-control-user" id="exampleInputPassword" name="password" placeholder="Password">
</div>
<div class="text-center">
<input type="submit" class = "btn btn-primary btn-user" value="Register">
</div>
</form>
By going through many sources on the internet(since i'm very very new to express js and im doing it as a part of my college project and since i can't consult any teachers for assistance during lockdown times) , I got to know that the validation part has to be implemented in user.js. Please help me with the code for validation and also displaying flash messages if field empty for atleast one field so that i can have a start atleast.
Thank you in advance
EDIT :
I Used the express-validator and ended up with the following changes -
var flash = require('connect-flash');
var app = express();
app.configure(function () {
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 } }));
app.use(flash());
});
app.get('/flash', function (req, res) {
// Set a flash message by passing the key, followed by the value, to
req.flash().
req.flash('info', 'There is an Error!')
res.redirect('/');
});
app.get('/', function (req, res) {
// Get an array of flash messages by passing the key to req.flash()
res.render('index', { messages: req.flash('info') });
});
const { check, validationResult } = require('express-validator');
router.post('/register', [
check('firstname', 'Please enter your first
name').exists().trim().escape().not().isEmpty(),
check('lastname', 'Please enter your last
name').exists().trim().not().isEmpty(),
check('username', 'Please enter an
email').exists().trim().not().isEmpty(),
check('usn', 'Please enter USN').exists().trim().not().isEmpty(),
check('course', 'Please enter Course').exists().trim().not().isEmpty(),
check('password', 'Please enter
password').exists().trim().not().isEmpty(),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
req.flash('message', `${errors}`);
res.redirect('/users/register');
} else {
userreg.register(
// eslint-disable-next-line new-cap
new userreg({
firstname: req.body.firstname,
lastname: req.body.lastname,
username: req.body.email,
usn: req.body.usn, // validate so that no space is taken or else
request modal wont work
course: req.body.course,
})),
res.render('submit-success', { username: req.body.firstname });
}
}
);
And as a result , the if (!errors.isEmpty()) is being invoked but there is no flash message being displayed. Am i missing something else ?

I am assuming you are using connect-flash.
const { check, validationResult } = require('express-validator');
router.post('/register', [
check('firstname', 'Please enter your first name').exists().trim().escape().not().isEmpty(),
check('lastname', 'Please enter your last name').exists().trim().not().isEmpty(),
check('email', 'Please enter an email').exists().trim().not().isEmpty(),
// ...
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
req.flash('message', `${errors}`);
res.redirect('register');
}
else {
// your code here!
}
});
You might want to consider creating middleware to handle validation errors, to make your code cleaner.

Related

I keep getting validator errors after submitting my form in Node.js using EJS templating engine

The code works well and perfectly when i use Postman to make the requests. But whenever I send the same request from the frontend I get this error.
Result {
formatter: [Function: formatter],
errors: [
{
value: undefined,
msg: 'Please enter your name',
param: 'name',
location: 'body'
},
{
value: '',
msg: 'Please enter a valid Email',
param: 'email',
location: 'body'
},
{
value: undefined,
msg: 'Please enter a password with 6 or more characters',
param: 'password',
location: 'body'
}
]
}
Here is my signup.ejs file.
<!doctype html>
<html lang="en">
<%- include('./partials/header.ejs')%>
<%- include('./partials/navbar.ejs')%>
<body>
<section class="form my-4 mx-5">
<div class="container">
<div class="row no-gutter">
<div class="col-lg-5">
<img src="/images/joyce-busola-Nnv0DHFG1Ds-unsplash.jpg" class="img-fluid">
</div>
<div class="col-lg-7 px-5 pt-5">
<h1 class="font-weight-bold py-3">Queue's</h1>
<h4>Register your new Account</h4>
<form action="/user/register" method="POST">
<div class="form-row">
<div class="col-lg-7">
<input type="text" id="name" class="form-control my-3 p-3" placeholder="your name">
</div>
</div>
<div class="form-row">
<div class="col-lg-7">
<input type="email" id="email" class="form-control my-3 p-3" placeholder="email address">
</div>
</div>
<div class="form-row">
<div class="col-lg-7 input-group">
<input type="password" class="form-control my-3 p-3" id="password" placeholder="password">
<div class="input-group-addon my-3 p-3">
</a><i class="bi bi-eye-slash" id="togglePassword"></i>
</div>
</div>
</div>
<div class="form-row">
<div class="col-lg-7">
<button type="submit" class="btn1 mt-3 mb-5">Register</button>
</div>
</div>
forgot password
<p>Already have an acount? Login here</p>
</form>
</div>
</div>
</div>
</section>
<%- include('./partials/footer.ejs')%>
</body>
</html>
Here is a copy of my signup controller object.
require('dotenv').config();
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const { createToken } = require('../middleware/auth_middleware');
const maxAge = 36000;
module.exports.register_post = async (req, res) => {
const { name, email, password } = req.body;
// Searching if User already exists
try {
let user = await User.findOne({ email });
if(user) return res.status(400).json({ errors : [{ msg: "User already exists"}],});
// Create User Object
user = new User({ name, email, password });
// Saving new User
await user.save();
// Creating User token with JWT
let payLoad = user._id;
const prize = createToken(payLoad);
res.cookie('jwt', prize, {
httpOnly: true,
maxAge : maxAge * 1000
});
res.status(201).json({
msg: `${user.email} successfully registered`,
name: user.name,
email: user.email});
console.log(user, prize);
}
catch (error) {
console.log(error.message);
res.status(500).send('Server error')
};
}
I made a middleware to handle validations with express-validator. Here is the validator file.
require('dotenv').config()
const { check, validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');
const maxAge = 36000;
const secret = process.env.JWT_SECRET;
const authValidator = () => {
return [
check('name', 'Please enter your name').not().isEmpty(),
check('email', 'Please enter a valid Email').trim().isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({min:6})
]};
const validateError = (req, res, next) => {
const errors = validationResult(req);
if(errors.isEmpty()) {
return next()
}
const extractedErrors = [];
errors.array().map(err => extractedErrors.push({ [err.param]: err.msg }))
console.log(errors);
return res.status(400).json({ errors: extractedErrors })
}
Here is the signup router file
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const { authValidator, validateError } = require('../middleware/auth_middleware');
router.get('/login', userController.login_get );
router.post('/login', userController.login_post );
router.get('/register', userController.register_get );
router.post('/register', authValidator(), validateError, userController.register_post );
module.exports = router;
Your input fields lack the name field. That's why the input data is not found in the body of the request.
for example for the email field:
<div class="form-row">
<div class="col-lg-7">
<input type="email" name="email" id="email" class="form-control my-3 p-3" placeholder="email address">
</div>
</div>

node.js POST empty req.body

I'm having trouble when I store data to MySQL database. I get a return in console blank.like this {}
here's route code :::app.post('/create', employeeController.create);
this is my controller code
exports.create = function(req, res) {
console.log((req.body));
const new_employee = new Employee(req.body);
//handles null error
if(req.body.constructor === Object && Object.keys(req.body).length === 0){
res.status(400).send({ error:true, message: 'Please provide all required field' });
}else{
Employee.create(new_employee, function(err, employee) {
if (err)
res.send(err);
res.json({error:false,message:"Employee added successfully!",data:employee});
});
}
};
this is my model code:
var db = require('../database');
//Employee object create
var Employee = function(employee){
this.name = employee.name;
this.email = employee.email;
this.position = employee.position;
};
Employee.create = function (newEmp, result) {
db.query("INSERT INTO employee set ?", newEmp, function (err, res) {
if(err) {
console.log("error: ", err);
result(err, null);
}
else{
console.log(res.insertId);
result(null, res.insertId);
}
});
};
module.exports= Employee;
my view code:
<form action="/create" enctype="multipart/form-data" method="POST">
<div class="row">
<div class="col-md-6">
<label>Name</label>
<input name="name" type="text" class="form-control" required>
</div>
<div class="col-md-6">
<label>Email</label>
<input name="email" type="email" class="form-control" required>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="exampleFormControlTextarea1" class="form-label">Position</label>
<input name="position" type="text" class="form-control" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Save</button>
<button type="submit" class="btn btn-primary">Cancel</button>
<br><br>
</form>
Im getting {} in log it means req.body is getting blank.
i have tried in app.js
app.use(bodyParser.json());
app.use(express.urlencoded({limit: '100mb',extended: false }));
but it's not working
Add app.use(bodyParser.urlencoded({ extended: false })) before your app.use(bodyParser.json());, and remove app.use(express.urlencoded({limit: '100mb',extended: false }));.
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
Then remove enctype="multipart/form-data" in the form. Because your form only have text input, so you could remove it. And bodyParser doesn't support multipart/form-data.

Why am I getting a 400 bad request error when submitting a form that I added inputs to?

Background: I have a node/express based web application that is basically a rating/database site for campgrounds. You can view the current working version here: https://radiant-eyrie-76078.herokuapp.com and the github here: https://github.com/HashSlingSlash/YelpCamp. I've just completed attempting to add user profiles by updating the signup form to include more information and adding a show page for each user. Now whenever I click the signup button (or send a post request to /register using postman) I get a 400 Bad Request error. If I then go to the home page I can sign in as the user that I attempted to register so the user is getting registered despite the bad request error. I have tried clearing my cache and browsing history and using other browsers, but it still won't work. I even tried removing all the changes I made to the form to make it just username and password again and it still did not work. I have tried fixing this and debugging for hours and I cannot understand what could be happening.
Here is my register page:
<%- include("./partials/header") %>
<div class="row">
<h1 class="login-header">Sign Up</h1>
<div class="login-form">
<form action="/register" method="POST">
<div class="form-group">
<label for="username">Username</label>
<input class="form-control" type="text" name="newUser[username]" placeholder="username" id="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input class="form-control" type="password" name="password" placeholder="password" id="password">
</div>
<div class="form-group">
<label for="firstName">First Name</label>
<input class="form-control" type="text" name="newUser[firstName]" placeholder="First Name" id="firstName">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input class="form-control" type="text" name="newUser[lastName]" placeholder="Last Name" id="lastName">
</div>
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="email" name="newUser[email]" placeholder="email#mail.com" id="email">
</div>
<div class="form-group">
<label for="avatar">Avatar</label>
<input class="form-control" type="text" name="newUser[avatar]" placeholder="avatar url" id="avatar">
</div>
<div class="form-group">
<label for="admin">Admin Key (Enter admin key here if you've been given one)</label>
<input class="form-control" type="text" name="adminKey" placeholder="********" id="admin">
</div>
<div class="form-group">
<button class="btn btn-lg btn-primary btn-block">Sign Up!</button>
</div>
</form>
Go Back
</div>
</div>
<%- include("./partials/footer") %>
Here are my index routes:
const express = require("express");
const router = express.Router();
const passport = require("passport");
const User = require("../models/user");
const Campground = require("../models/campground");
//root route
router.get("/", (req, res) =>{
res.render("landing");
});
//show register form
router.get("/register", (req, res) =>{
res.render("register", {page: "register"});
});
//handle sign up logic
router.post("/register", (req, res) =>{
const newUser = new User(req.body.newUser);
if(req.body.adminKey === "secret"){
newUser.isAdmin = true;
}
User.register(newUser, req.body.password, (err, user) =>{
if(err){
return res.render("register", {"error": err.message});
}
passport.authenticate("local")(req, res, () =>{
req.flash("success", "Welcome to YelpCamp " + user.username);
res.redirect("/campgrounds");
});
});
});
//login form
router.get("/login", (req, res) =>{
res.render("login", {page: "login"});
});
//login logic
router.post("/login",
passport.authenticate("local",
{
failureRedirect: "/login",
failureFlash: true
}), (req, res) =>{
req.flash("success", "Welcome back " + req.user.username);
res.redirect("/campgrounds");
});
//logout
router.get("/logout", (req, res) =>{
req.logOut();
req.flash("success", "Logged you out!");
res.redirect("/campgrounds");
});
//user profile
router.get("/user/:id", (req, res) =>{
User.findById(req.params.id, (err, foundUser) =>{
if(err){
req.flash("error", "Something went wrong");
return res.redirect("back");
}
Campground.find().where("author.id").equals(foundUser._id).exec((err, campgrounds) =>{
if(err){
req.flash("error", "Something went wrong");
return res.redirect("back");
}
res.render("users/show", {user: foundUser, campgrounds: campgrounds});
});
});
});
module.exports = router;
Here is my User schema:
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema({
username: String,
password: String,
firstName: String,
lastName: String,
email: String,
avatar: String,
isAdmin: {type: Boolean, default: false}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
I've tried fixing this for hours and would appreciate any help!
I finally figured out the issue. I had to change the name attribute on the form inputs to just username, password, email, etc. I believe passport local mongoose expects to receive the data according to those key value pairs specifically, although I am unsure as to the exact reason why this is necessary since I end up giving the data as an object either way. In case anyone wants to see it here is my updated and working code:
Register form:
<%- include("./partials/header") %>
<div class="row">
<h1 class="login-header">Sign Up</h1>
<div class="login-form">
<form action="/register" method="POST">
<div class="form-group">
<label for="username">Username</label>
<input class="form-control" type="text" name="username" placeholder="username" id="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input class="form-control" type="password" name="password" placeholder="password" id="password">
</div>
<div class="form-group">
<label for="firstName">First Name</label>
<input class="form-control" type="text" name="firstName" placeholder="First Name" id="firstName">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input class="form-control" type="text" name="lastName" placeholder="Last Name" id="lastName">
</div>
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="email" name="email" placeholder="email#mail.com" id="email">
</div>
<div class="form-group">
<label for="avatar">Avatar</label>
<input class="form-control" type="text" name="avatar" placeholder="avatar url" id="avatar">
</div>
<div class="form-group">
<label for="admin">Admin Key (Enter admin key here if you've been given one)</label>
<input class="form-control" type="text" name="adminKey" placeholder="********" id="admin">
</div>
<div class="form-group">
<button class="btn btn-lg btn-primary btn-block">Sign Up!</button>
</div>
</form>
Go Back
</div>
</div>
<%- include("./partials/footer") %>
Route:
//handle sign up logic
router.post("/register", (req, res) =>{
const newUser = new User({
username: req.body.username,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
avatar: req.body.avatar
});
if(req.body.adminKey === "secret"){
newUser.isAdmin = true;
}
User.register(newUser, req.body.password, (err, user) =>{
if(err){
return res.render("register", {"error": err.message});
}
passport.authenticate("local")(req, res, () =>{
req.flash("success", "Welcome to YelpCamp " + user.username);
res.redirect("/campgrounds");
});
});
});

bcrypt.compareSync is always returning false

I verified that in my db I am saving the username and hash of the password. I am able to retrieve the name from the db, however when I check the password it always returns false. Not sure what is wrong.
Here is my HTML
<div ng-controller="userController">
<div class=user>
<form name="login_form">
<h2 class>Login</h2>
<h3 class = "login_page">UserName</h3>
<input ng-model="user" type="text" ng-minlength="1" required>
<h3 class = "login_page">Password</h3>
<input ng-model="password" type="password" name="password" ng-minlength="4" required>
<input type="submit" value="Login" ng-click="login()" >
<div ng-if ="login_form.$submitted" ng-messages="login_form.password.$error" style="color:maroon" role="alert">
<div ng-message="minlength">Your field is too short</div>
</div>
<p ng-if="error">Username or login is incorrect</p>
</form>
</div>
<div class=user>
<form name = "register_form">
<h2 class>Register</h2>
<h3 class = "login_page">UserName</h3>
<input ng-model="reg.name" type="text" required>
<h3 class = "login_page">Password</h3>
<input ng-model="reg.password" type="password">
<input type="submit" value="Register" ng-click="register()" required >
<div ng-if ="login_form.$submitted" ng-messages="login_form.password.$error" style="color:maroon" role="alert">
<div ng-message="minlength">Your field is too short</div>
</div>
<p ng-if="duplicate">That user name is taken, please choose another</p>
<p ng-if="correct">Registration Succesfull</p>
</form>
</div>
</div>
Here is my controller on the server side
var mongoose = require('mongoose'),
Todo = mongoose.model('Todo');
Login = mongoose.model('Login');
var bcrypt = require('bcrypt');
var name = ""
module.exports = (function(){
return {
save_name:function(req, res){
req.session.user = req.body.user
Login.findOne({name: req.body.user},
function(err, user) {
if(user){
console.log(user.password);
console.log( bcrypt.compareSync(req.body.password, user.password));
res.json({'error': false});
}else {
res.json({'error': true});
}
})
}, //end of save name method
register:function(req, res){
bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(8));
login = new Login({
name:req.body.user,
password: bcrypt.genSaltSync(8)
})
login.save(function(err){
if(err){
res.json({'error': true});
} else {
res.json({'sucess': true})
}
})
} // end of register user function
}
})();
You're saving a generated salt as the password instead of the actual hash itself. Also, explicitly calling genSalt*() is unnecessary. Lastly, you really should use the async functions instead, to avoid unnecessarily blocking the event loop. So with all of this in mind, you may end up with something like:
module.exports = {
save_name: function(req, res) {
req.session.user = req.body.user;
Login.findOne({ name: req.body.user },
function(err, user) {
if (err)
return res.json({ error: true });
bcrypt.compare(req.body.password,
user.password,
function(err, valid) {
res.json({ error: !!(err || !valid) });
});
});
}, // end of save name method
register: function(req, res) {
bcrypt.hash(req.body.password, 8, function(err, hash) {
if (err)
return res.json({ error: true });
login = new Login({
name: req.body.user,
password: hash
})
login.save(function(err) {
res.json({ error: !!err });
})
});
} // end of register user function
};
Despite other answers, if it is still not resolving your issue. Try by applying the toString() when passing the password upon login like this.
req.body.password.toString();
The immediate cause of your bug is in register you should be using bcrypt.hashSync(myPlaintextPassword, saltRounds) instead of genSaltSync. Fixing that should make things "work".
However, you need to recode all this to use the async bcrypt APIs or your application will respond very poorly under load (like crippled and unusable, not just "slow"). General rule: no sync calls in a node.js server.

Flash message in node js

I want a login page where the user enters their username and password, if it match then login is successful otherwise I want to show the flash message.
I've tried many different ways of doing this but couldn't find the correct way.
router.get('/login', function (req, res) {
res.render('login', {
layout: 'layouts/main/subpages',
topimagetype: 'home',
title: 'Log In',
message: req.flash('loginMessage')
});
});
You use the connect flash which is really simple to use.
Install the connect-flash via npm.
Here i give you example might be its works for you.
index.js // Router
router.get('/login', function (req, res) {
res.render('login', {
layout: 'layouts/main/subpages',
topimagetype: 'home',
title: 'Log In',
message: req.flash('loginMessage')
});
});
Passport.js // Configuration file
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function (req, email, password, done) {
pool.getConnection(function (err, connection) {
connection.query("SELECT * FROM user WHERE user_name = '" + email + "'", function (err, rows) {
if (err)
return done(err);
if (!rows.length) {
return done(null, false, req.flash('loginMessage', 'No user found.'));
}
if (!(passwordHash.verify(password, rows[0].password)))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
var userType={};
userType.super=false;
userType.admin=false;
userType.normal=false;
if (rows[0].Roll_id == 1) {
userType.super=true;
} else if (rows[0].Roll_id == 2) {
userType.admin=true;
}
else {
userType.normal=true;
}
rows[0].type=userType;
rows[0].created_date = rows[0].created_date.toDateString();
return done(null, rows[0]);
});
});
}));
Html View
<div class="login">
<div class="login-screen">
<div class="container">
{{#message}}
<div style="margin-left: 35%; width: 30%;">
<div class="alert alert-danger">{{ message }}</div>
</div>
{{/message}}
<section class="main">
<form action="/login" method="post" role="form" class="form-1">
<p class="field">
<input type="text" name="email" placeholder="email" id="login-name">
<i class="icon-user icon-large"></i>
</p>
<p class="field">
<input type="password" name="password" placeholder="password" id="login-pass">
<i class="icon-lock icon-large"></i>
</p>
<p class="submit">
<button type="submit" name="submit"><i class="icon-arrow-right icon-large"></i></button>
</p>
</form>
</section>
</div>

Resources