POST requests return null. The req.body..... values are undefined. I tried to verify it arrives to the server in JSON format, but
i am not sure I am doing it in a right way.... :-(
Thanx a lot for help, amigos!
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const bodyParser = require('body-parser');
const cors = require("cors");
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
var app = express();
app.set('view engine', 'ejs')
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(bodyParser.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
const { MongoClient } = require("mongodb");
const uri = "mongod...the string is ok....y";
const client = new MongoClient(uri);
const database = client.db('holdocsDB');
const records = database.collection('records');
app.post("/users", async (req, res) => {
console.log('hola pooost');
console.log("req.body.age: " + req.body.age);
try {
// create a document to insert
const newRecord = {
firstName: req.body.firstName,
age: req.body.age
}
const result = await records.insertOne(newRecord);
console.log(`A document was inserted with the _id: ${result.insertedId}`);
// } finally {
// await client.close();
// }
} catch {
(e) => (console.error(e));
}
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.json('error');
});
module.exports = app;
When I hardcode some values to be posted in express POST route (instead of extracting the values from req.body) - it works well.
function RecordSearchBar() {
const [form, setForm] = useState({
firstName: "",
age: 0
});
// const navigate = useNavigate();
function updateForm(value) {
return setForm((prev) => {
return { ...prev, ...value };
});
}
async function onSubmit(e) {
e.preventDefault();
const newPerson = { ...form };
await fetch("http://localhost:3001/users/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newPerson),
})
.catch(error => {
window.alert(error);
return;
});
setForm({ firstName: "", age: ""});
// navigate("/users");
}
return (
<>
<h2>Search records:</h2>
<form onSubmit={onSubmit} id='search-form'>
<div className="form-group">
<label htmlFor="firstName">First name</label>
<input
type="text"
className="form-control"
id="name"
value={form.name}
onChange={(e) => updateForm({ name: e.target.value })}
/>
</div>
<div className="form-group">
<label htmlFor="age">Age</label>
<input
type="number"
className="form-control"
id="position"
value={form.position}
onChange={(e) => updateForm({ position: e.target.value })}
/>
</div>
<div className="form-group">
<input
type="submit"
value="Add record"
className="btn btn-primary"
/>
</div>
</form>
</>
Solved! I had to correct the type conversion of request body using JSON.parse() and JSON.stringify():
app.post("/users", async (req, res) => {
console.log("req.body: " + JSON.stringify(req.body));
let newRecordStringified = JSON.stringify(req.body)
try {
// create a document to insert
const newRecord = {
firstName: req.body.firstName,
age: req.body.age
// firstName: "Mamile",
// age: 33
}
const result = await records.insertOne(JSON.parse(newRecordStringified));
console.log(`A document was inserted with the _id: ${result.insertedId}`);
// } finally {
// await client.close();
// }
} catch {
(e) => (console.error(e));
}
})
I can't seem to get my csurf token to work in my pop up strictly css modal, for authentication purposes. It works fine in a page (not in modal). I am using Pug view engine, express and cookie parser. Below is the relevant code, any suggestions would be appreciated, thank you.
layout.pug
a(href="#open-modal")
span Login
div(id="open-modal" class="modal-window")
div
a(href="#" title="Close" class="modal-close") Close
h1 Login
div
+errorSummaryList(errors)
form(method="post" action="/get" class="nav__login")
input(type="hidden" name="_csrf" value=csrfToken)
label(for="username") Username:
input(type="text" name="username" id="username")
label(for="password") Password:
input(type="password" name="password" id="password")
button Submit
button Demo User
auth.js
const { User } = require('./db/models');
function loginUser(req, res, user){
req.session.auth = {
userId: user.id
};
req.session.save(function () {
res.redirect("/");
});
console.log(req.session)
};
async function restoreUser(req, res, next){
if(req.session.auth){
let { userId }= req.session.auth;
try{
let user = await User.findByPk(userId);
if(user){
res.locals.authenticated = true;
res.locals.user = user;
console.log(res.locals)
next();
}
} catch(error){
res.locals.authenticated = false;
next(error);
}
}
res.locals.authenticated = false;
next();
}
function logoutUser(req,res){
delete req.session.auth;
}
users.js
router.get('/login', csrfProtection, asyncHandler(async(req, res, next)=> {
let user = User.build();
res.render('user-login',{user, csrfToken: req.csrfToken()})
}));
router.post('/login',loginValidators, csrfProtection, asyncHandler(async(req, res, next)=>{
const { username, password } = req.body;
let errors = [];
const validationErrors = validationResult(req);
if(validationErrors.isEmpty()){
const user = await User.findOne({
where:{
username
}
})
if(user){
console.log('found user')
const isVerified = await bcrypt.compare(password, user.password.toString())
if(isVerified){
console.log("verified")
loginUser(req, res, user)
return;
}
errors.push("Username and/or password are incorrect. Try again. ");
}
}
validationErrors.array().map(err => errors.push(err.msg));
res.render('user-login',{errors, csrfToken: req.csrfToken()})
}));
app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const { sequelize } = require('./db/models');
const session = require('express-session');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const { restoreUser } = require('./auth');
const { sessionSecret } = require('./config');
const app = express();
// view engine setup
app.set('view engine', 'pug');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(sessionSecret));
app.use(express.static(path.join(__dirname, 'public')));
// set up session middleware
const store = new SequelizeStore({ db: sequelize });
app.use(
session({
secret: sessionSecret,
store,
saveUninitialized: false,
resave: false,
})
);
I created a api route named "/add" that renders a file named "additional-user-info" with crsf token and it works fine
app.js
const express = require("express");
require("dotenv").config();
const mongoose = require("mongoose");
const authRoutes = require("./routes/authRoutes");
const cookieParser = require("cookie-parser");
var csrf = require("csurf");
var csrfProtection = csrf({ cookie: true });
const {
requireAuth,
checkUser,
displayallusers,
deleteUser,
searchuser,
isAdmin,
} = require("./middleware/authMiddleware");
const mongoSantize = require("express-mongo-sanitize");
const xss = require("xss-clean");
const app = express();
// middleware
app.disable("x-powered-by");
app.use(express.static("public"));
app.use(mongoSantize());
app.use(xss());
app.use(express.json());
app.use(cookieParser());
//handling wrong invlid json syntax
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && "body" in err) {
//console.error(err);
return res.status(400).send("Invalid Json formatting"); // Bad request
}
});
// view engine
app.set("view engine", "ejs");
// database connection
const mongoUsername = process.env.MONGO_USERNAME;
const mongoPassword = process.env.MONGO_PASSWORD;
const dbURI =
"mongodb+srv://" +
mongoUsername +
":" +
mongoPassword +
"#boioboi/real-auth?retryWrites=true&w=majority";
mongoose
.connect(dbURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then((result) => app.listen(3000))
.catch((err) => console.log(err));
// routes
app.get("*", checkUser);
app.get("/add", csrfProtection, (req, res) => {
console.log("zeeeeeeeee");
console.log(req.csrfToken());
res.render("additional-user-info", { csrfToken: req.csrfToken() });
});
app.get("/", (req, res) => res.render("home"));
app.get("/smoothies", requireAuth, (req, res) => res.render("smoothies"));
app.use(authRoutes);
Website has a functionality that if a user creates an account or logins into his account and has certian database fields empty website would render same "additional-user-info" page to get his info
This is a function that checks if a user is valid or not and renders "additional-user-info" page if certain fields are empty
authMiddleware
const checkUser = (req, res, next) => {
const token = req.cookies.jwt;
if (token) {
jwt.verify(token, jwt_secret, async (err, decodedtoken) => {
if (err) {
console.log(err.message);
res.locals.user = null;
next();
} else {
console.log(decodedtoken);
let user = await User.findById(decodedtoken.id);
res.locals.user = user;
if (
(user.additionalinfo.fullname == "") |
(user.additionalinfo.address == "") |
(user.additionalinfo.city == "") |
(user.additionalinfo.district == "") |
(user.additionalinfo.propertytype == "") |
(user.additionalinfo.adharcard == "") |
(user.additionalinfo.pancard == "")
) {
return res.render("additional-user-info"); //crsf-token is not defined error here
}
next();
}
});
} else {
res.locals.user = null;
next();
}
};
But when it renders "additional-user-info" page from checkUser() function it gives error saying "csrfToken is not defined". How should i fix this issue?
I am trying to create e registration for a new user with profile picture upload. But my form data is not passing to the route. I have added body-parser middleware but still, it seems something is wrong and I cannot find the reason.
Error:
D:\temp_project\smpms\routes\users.js:118
if (err) throw err;
^
Error: Illegal arguments: undefined, string
at _async (D:\temp_project\smpms\node_modules\bcryptjs\dist\bcrypt.js:214:46)
at Object.bcrypt.hash (D:\temp_project\smpms\node_modules\bcryptjs\dist\bcrypt.js:220:13)
at D:\temp_project\smpms\routes\users.js:117:28
at Immediate.<anonymous> (D:\temp_project\smpms\node_modules\bcryptjs\dist\bcrypt.js:153:21)
at processImmediate (internal/timers.js:456:21)
[nodemon] app crashed - waiting for file changes before starting...
app.js
const express = require("express");
const expressLayouts = require("express-ejs-layouts");
const mongoose = require("mongoose");
const passport = require("passport");
const flash = require("connect-flash");
const session = require("express-session");
const multer = require('multer');
const path = require('path');
var dotenv = require('dotenv').config();
const bodyParser = require('body-parser');
const app = express();
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.use(express.static("public"));
// Passport Config
require("./config/passport")(passport);
// DB Config
const db = require("./config/keys").mongoURI;
// Connect to MongoDB
mongoose
.connect(db, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("MongoDB Connected"))
.catch((err) => console.log(err));
// EJS
app.use(expressLayouts);
app.set("view engine", "ejs");
// Express body parser
app.use(express.urlencoded({ extended: true }));
// Express session
app.use(
session({
secret: "secret",
resave: true,
saveUninitialized: true,
})
);
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
app.use(flash());
// Global variables
app.use(function (req, res, next) {
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
res.locals.error = req.flash("error");
next();
});
// Routes
app.use("/", require("./routes/index.js"));
app.use("/users", require("./routes/users.js"));
const PORT = process.env.PORT || 5000;
app.listen(PORT, console.log(`Server started on port ${PORT}`));
User.js - User model
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
avatar:{
type:String,
required:true
},
date: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
User.js route:
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const passport = require("passport");
const multer = require('multer');
const path = require('path');
const bodyParser = require('body-parser')
const app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
// Load User model
const User = require("../models/User");
const {forwardAuthenticated} = require("../config/auth");
// Login Page
router.get("/login", forwardAuthenticated, (req, res) => {
res.render("login", {title: "Login", layout: "layout"});
});
// Register Page
router.get("/register", forwardAuthenticated, (req, res) => {
res.render("register", {title: "Register", layout: "layout"});
});
// Register
router.post("/register", (req, res) => {
//const {name, email, password, password2||avatar} = req.body;
const name = req.body.name
const email = req.body.email
const password = req.body.password;
const password2 = req.body.password2;
const avatar = req.body.avatar;
let errors = [];
//
// if (!name || !email || !password || !password2) {
// errors.push({msg: "Please enter all fields"});
// }
// if (password && password.length < 6) {
// errors.push({msg: "Password must be at least 6 characters"});
// }
// if (password != password2) {
// errors.push({msg: "Passwords do not match"});
// }
if (errors.length > 0) {
res.render("register", {
errors,
name,
email,
password,
password2,
title: "Register",
layout: "Layout",
});
} else {
User.findOne({email: email}).then((user) => {
if (user) {
errors.push({msg: "Email already exists"});
res.render("register", {
errors,
name,
email,
password,
password2,
title: "Register",
layout: "Layout",
});
} else {
const newUser = new User({
name,
email,
password,
});
//Set The Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + newUser._id + path.extname(file.originalname));
}
});
// Init Upload
const upload = multer({
storage: storage,
limits: {fileSize: 1000000},
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
}
}).single('avatar');
// Check File Type
function checkFileType(file, cb) {
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb('Error: Images Only!');
}
}
console.log(newUser);
newUser.avatar = storage;
console.log(newUser);
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then((user) => {
req.flash(
"success_msg",
"You are now registered and can log in"
);
res.redirect("/users/login");
})
.catch((err) => console.log(err));
});
});
}
});
}
});
// Login
router.post("/login", (req, res, next) => {
passport.authenticate("local", {
successRedirect: "/dashboard",
failureRedirect: "/users/login",
failureFlash: true,
})(req, res, next);
});
// Logout
router.get("/logout", (req, res) => {
req.logout();
req.flash("success_msg", "You are logged out");
res.redirect("/users/login");
});
module.exports = router;
Register.ejs is the file where the form is not passing data to the route.
<div class="row mt-5">
<div class="col-md-6 m-auto">
<div class="card card-body">
<h1 class="text-center mb-3">
<i class="fas fa-user-plus"></i> Register
</h1>
<% include ./partials/messages %>
<%= typeof msg != 'undefined' ? msg : '' %>
<form action="/users/register" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="name">Name</label>
<input
type="name"
id="name"
name="name"
class="form-control"
placeholder="Enter Name"
value="<%= typeof name != 'undefined' ? name : '' %>"
/>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
class="form-control"
placeholder="Enter Email"
value="<%= typeof email != 'undefined' ? email : '' %>"
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
class="form-control"
placeholder="Create Password"
value="<%= typeof password != 'undefined' ? password : '' %>"
/>
</div>
<div class="form-group">
<label for="password2">Confirm Password</label>
<input
type="password"
id="password2"
name="password2"
class="form-control"
placeholder="Confirm Password"
value="<%= typeof password2 != 'undefined' ? password2 : '' %>"
/>
</div>
<div class="form-group">
<label for="">Profile Picture</label>
<input type="file" name="avatar" id="" class="form-control file-path validate">
</div>
<button type="submit" class="btn btn-primary btn-block">
Register
</button>
</form>
<p class="lead mt-4">Have An Account? Login</p>
</div>
</div>
</div>
It seems that you aren't using multer correctly. Usually multer configuration is set globally and above all the APIs and then we pass that middleware function generated by multer to that specific API which has multipart/form-data header in its request. You have to move your configuration for multer, outside of your API, then pass the upload as a middleware to your API. Also from your clientside, you have to pass data properly. A good answer about sending JSON alongside the File with FormData object can be found here. Also you can add text fields in FormData object from clientside and receive it as object in req.body according to multer documentation. Just make sure you're passing data from clientside to server correctly. Here is your router with multer middleware:
const express = require("express");
const router = express.Router();
const fs = require('fs');
const bcrypt = require("bcryptjs");
const passport = require("passport");
const multer = require('multer');
const path = require('path');
const bodyParser = require('body-parser')
const app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
// Load User model
const User = require("../models/User");
const { forwardAuthenticated } = require("../config/auth");
// Login Page
router.get("/login", forwardAuthenticated, (req, res) => {
res.render("login", { title: "Login", layout: "layout" });
});
// Register Page
router.get("/register", forwardAuthenticated, (req, res) => {
res.render("register", { title: "Register", layout: "layout" });
});
const storage = multer.diskStorage({
destination: './public/uploads/'
});
// Init Upload
const upload = multer({
storage: storage,
limits: { fileSize: 1000000 },
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
}
});
// Check File Type
function checkFileType(file, cb) {
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb('Error: Images Only!');
}
}
// Register
router.post("/register", upload.single('avatar'), (req, res) => {
//const {name, email, password, password2||avatar} = req.body;
const name = req.body.name
const email = req.body.email
const password = req.body.password;
const password2 = req.body.password2;
let errors = [];
//
// if (!name || !email || !password || !password2) {
// errors.push({msg: "Please enter all fields"});
// }
// if (password && password.length < 6) {
// errors.push({msg: "Password must be at least 6 characters"});
// }
// if (password != password2) {
// errors.push({msg: "Passwords do not match"});
// }
if (errors.length > 0) {
res.render("register", {
errors,
name,
email,
password,
password2,
title: "Register",
layout: "Layout",
});
} else {
User.findOne({ email: email }).then((user) => {
if (user) {
errors.push({ msg: "Email already exists" });
res.render("register", {
errors,
name,
email,
password,
password2,
title: "Register",
layout: "Layout",
});
} else {
const directory = "/images/";
const newUser = new User({
name,
email,
password,
avatar: `./public/uploads/${req.file}`
});
const filePath = path.join(__dirname, "../public/uploads/");
fs.rename(filePath + req.file.filename, req.file.fieldname + '-' + newUser._id + path.extname(req.file.originalname), (error) => {
if (error) {
return console.log(`Error: ${error}`);
}
});
newUser.avatar = 'public/uploads/' + req.file.fieldname + '-' + newUser._id + path.extname(req.file.originalname);
console.log(newUser);
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then((user) => {
req.flash(
"success_msg",
"You are now registered and can log in"
);
res.redirect("/users/login");
})
.catch((err) => console.log(err));
});
});
}
});
}
});
// Login
router.post("/login", (req, res, next) => {
passport.authenticate("local", {
successRedirect: "/dashboard",
failureRedirect: "/users/login",
failureFlash: true,
})(req, res, next);
});
// Logout
router.get("/logout", (req, res) => {
req.logout();
req.flash("success_msg", "You are logged out");
res.redirect("/users/login");
});
module.exports = router;
It will save your file without any problem (if your configuration about directory and filename was okay).
I'm trying to access the body of the request in a middleware in order to perform it only if a specific field has changed (ereasing pictures only if new pictures got uploaded), but I'm not able to access the body.
I've tried to install and configure cors as well as reconfiguring body-parser in the route-specific file, as well as shuffling around the code, but nothing has helped (this is what was suggested in other questions).
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const multer = require('multer');
const glob = require('glob');
const fs = require('fs');
const _ = require('lodash');
const urlencodedParser = bodyParser.urlencoded({ extended: false });
//MIDDLEWARE
router.use("/immobili/:_id/edit", urlencodedParser, function (req, res, next) {
console.log(req.body);
const requestedId = req.params._id;
Immobile.findOne({ _id: requestedId }, (err, immobile) => {
if (err) return console.error(err);
immobile.immagini = [];
cancellaFoto(immobile);
if (this.cancellato) {
return setTimeout(next, 1000);
} else {
return console.log("Aborted");
}
});
});
//EDIT PUT ROUTE
router.put("/immobili/:_id/edit", upload.array('immaginePrincipale', 30), function (req, res) {
const requestedId = req.params._id;
const dati = req.body;
const proprietaImmagini = req.files;
const immagini = proprietaImmagini.map(function (immagine) {
//console.log(immagine.path);
return immagine.path;
});
let vetrina;
req.body.vetrina === 'on' ? vetrina = true : vetrina = false;
Immobile.findOneAndUpdate({ _id: requestedId }, {
numeroScheda: dati.numeroScheda,
//[... ALL DATA ... ]
immagini: immagini,
}, function (err, updatedImmobile) {
if (err) return console.error(err);
res.redirect("/immobili/" + req.body.categoria + "/" + _.toLower(req.body.localita) + "/" + requestedId);
});
});
This is the form I'm using to send the data:
<form action="/immobili/<%= immobile._id %>/edit?_method=PUT" method="POST"
enctype="multipart/form-data">
//[ ... FORM INPUTS ... ]
<input type="file" name="immaginePrincipale" multiple="multiple" id="immaginePrincipale"></input>
<input type="submit" value="Pubblica">
</form>
I would expect to access the body of the request in the middleware but I get only an empty object in return.