I am trying to fix a form handler that always throws a 400 error after middleware validation.
The middleware validation goes like this:
const contactValidate = [
check('name')
.exists()
.trim()
.escape()
.not()
.isEmpty()
.withMessage('Name can not be empty!')
.bail()
.isLength({min: 2})
.withMessage('Minimum 2 characters required!')
.bail()
.isAlpha('en-US', {ignore: ' '})
.withMessage('Name can only have letters and spaces')
.bail(),
check('phone')
.trim()
.escape()
.not()
.isEmpty()
.withMessage('Phone can not be empty!')
.bail()
.isMobilePhone()
.withMessage('Must be a phone number.')
.bail(),
check('email')
.trim()
.normalizeEmail()
.not()
.isEmpty()
.withMessage('Invalid email address!')
.bail(),
check('note')
.exists()
.trim()
.escape()
.not()
.isEmpty()
.withMessage('Message can not be empty!')
.bail(),
check('botCheck')
.isIn(["on"])
.withMessage("Bot Check Failed")
.bail(),
function (req, res, next) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
},
];
It is supposed to hand off errors to this ajax call:
const form = document.getElementById('contact-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(form);
fetch('/contact/', {
method: 'POST',
body: formData,
mode: 'cors',
contentType: "application/json; charset=utf-8"
})
.then((response) => {
response.json();
})
.then((response) => {
// handle errors
if (response.errors) {
response.errors.forEach(({ msg }) => {
document.getElementById('status').innerHTML += `<p class="note note-danger">${msg}</p>`
});
return;
}
// If mail was sent successfully, reset all elements with attribute 'name'
const values = document.querySelectorAll('[name]');
values.forEach( value => {
value.textContent = '';
});
document.getElementById('status').innerHTML = `<p class="note note-success">${response.msg}</p>`;
})
.catch((err) => {
document.getElementById('status').innerHTML += `<p class="note note-danger">${err}</p>`
})
.finally(()=> {
setTimeout(()=> {
document.getElementById('status').innerHTML = '';
}, 2000)
})
});
The start of the form has the right HTMl elements:
<form id="contact-form" class="needs-validation" novalidate enctype="multipart/form-data">
I can not get it past the 400 error that says "TypeError: Cannot read properties of undefined (reading 'errors')". I am not sure if it's my server side validation in express-validator or the AJAX call that is incorrect.
Ok, I realized after looking back that it was the middleware that step 1 of the problem and then the Ajax handle I was using was formatted incorrectly to handle the response.
The correct Middleware should be:
module.exports = [
body("name")
.trim()
.notEmpty().withMessage('Name is required.')
.isAlpha('en-US', {ignore: '\s'}).withMessage('Name can only have letters.')
.bail(),
body("email")
.trim()
.notEmpty().withMessage('Email is required.')
.isEmail().withMessage('Invalid email address.')
.bail(),
body("phone")
.trim()
.notEmpty().withMessage('Phone number is required.')
.isMobilePhone('en-US').withMessage("Please make sure it is a valid phone number")
.bail(),
body("note")
.trim()
.notEmpty().withMessage('Message is required.')
.bail(),
body("botCheck").isIn(["on"]).withMessage("Bot Check Failed"),
function (req, res, next) {
const errors = validationResult(req);
console.log("Validation Result:", errors);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
},
];
Then the AJAX to handle that response should be:
const form = document.getElementById('contact-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(form);
for (var pair of formData.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
console.log(formData); // Log form data
fetch('/contact', {
method: 'POST',
body: formData,
mode: 'cors'
})
.then((response) => {
return response.json(); // Otherwise, proceed as usual
})
.then((response) => {
// handle errors
if (response.errors) {
response.errors.forEach(({ msg }) => {
document.getElementById('status').innerHTML += `<p class="note note-danger">${msg}</p>`;
});
return;
}
// If mail was sent successfully, reset all elements with attribute 'name'
const values = document.querySelectorAll('[name]');
values.forEach(value => {
value.textContent = '';
});
document.getElementById('status').innerHTML = `<p class="note note-success">${response.msg}</p>`;
// Clear the form
form.reset();
})
.catch((err) => {
document.getElementById('status').innerHTML += `<p class="note note-danger">${err}</p>`;
})
.finally(() => {
setTimeout(() => {
document.getElementById('status').innerHTML = '';
}, 2000);
});
});
I realize now that the 400 error was due to malformed express-validator code and I just needed to process the formData in the Ajax and it worked perfectly.
Thanks for the help.