Form validation does not work

Omar Martin
Omar Martin used Ask the Experts™
on
I am using  Joi (Happy) module in node.js to validate entries into a form to a mysql database.  However, the validation does not work. I am receiving a "TypeError: Cannot read property 'path' of undefined" every time I leave the file field empty.  Nothing occurs when I leave any other field empty.  In other words, the validation simply does not work and no field message is being sent to the user based on the entry criteria. Please see the backend and the frontend code using sequelize. Without validation, the form works fine.

FRONTEND CODE WITH FORM:

<!DOCTYPE html>
<html lang="en">
<head>
  <!--JQuery UI: default css (smoothness)(always first)-->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css"> 


  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>

  <!--JQuery (always first)-->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>

<!--JQuery UI core (always first)-->
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>

<!--JQuery UI(after JQuery UI core) - Controls Dialog Modal -->
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> 

</head>
<body>
  <!---(8)-->
  <h3>Inserting Data into the database</h3>
  <h3>Data List</h3>
  <div id="storesF">
  <div id="modalStationForm">
      
    <!--form id="stationForm">-->
    <form id="stationForm" method="POST" action="/api/station" enctype="multipart/form-data">
      <input type="hidden" id="id" name="record_id">

      <label>Store Name:</label>
      <!--When submitting a form, the name value is important and is used in php file, while the id is used for css and javascript(below)-->
      <input type="text" name="station" id="store" size="40" placeholder="Enter Store Name">
      <br><br>
      
      <label for="file">Profile Photo:</label>
      <input type="file" name="image" id="image" onChange={this.uploadFile}>
      <br><br> 

      <label>Address:</label>
      <input type="text" name="address" id="location" size="40" placeholder="Enter the store's address">
      <br><br>

      <label>Store Sales:</label>
      <input type="number" name="monthlycstoresales" id="sales" size="20">
      <br><br>

      <label>Operator:</label>
      <input type="text" name="operator" id="dealer" size="40" placeholder="Enter the operator's name">
      <br><br>

      <label>Top SKU item:</label>
      <input type="text" name="topsku" id="topitem" size="40" placeholder="Enter the store's top sku item">

      
      <br><br>

      <input id="insertD" type="submit" value="Insert">
      <input id="insertE" type="submit" value="Edit">
      <div id="statusF"></div>
      </form>
   
      
      </div>   <!--id=storesF-->
      
      </div>  <!--id=modalStationForm-->
      
      
      </div>  <!--//id=container-->
      </div>  <!--//id=background-->

      <script>
      $(document).on('click', '.insert', function (e) {
    //$('#insertD').click(() => {
    e.preventDefault();
    $("#insertE").remove();
    $("#insertD").show();
    $("#stationForm")[0].reset();
    $("#modalStationForm")
      .dialog("option", "title", "Inserting New Station")
      .dialog("open");
  });
  
  $("#stationForm").on('submit',  function (e) { 
    e.preventDefault();
    console.log(this);
    var data = new FormData(this);
    console.log(data);
    $.ajax({
      url: '/api/station',
      contentType: false,
      processData: false,
      type:'POST',
      dataType: 'JSON',
      data: data,
      success: (data) => {
//        console.log(JSON.stringify(data, null,2));
        $('#result').text(JSON.stringify(data, null,2));
        alert('Entry Record Successfully Submitted');
      },
    });
 
  });

      
      </script>


</body>
</html>

Open in new window



PLEASE FIND THE BACKEND CODE:

const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");


//Setting Storage Engine

const storage = multer.diskStorage({
	destination: function (req, file, cb) {
    cb(null, "uploads/");
	},
	filename: function (req, file, cb) {
			cb(
			null,
			`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
		);
  }
});

const fileFilter = function (req, file, cb) {
	if (
		file.mimetype === "image/jpeg" ||
		file.mimetype === "image/png" ||
		file.mimetype === "image/gif"
	) {
		cb(null, true);
	} else {
		cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
	}
};

const upload = multer({
	storage: storage,
	limits: {
		fileSize: 1024 * 1024 * 5
	},
	fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////

router.post("/station",upload.single("image"), (req, res, next) => {
  
  
  let newStation = req.body.station;
  let newImage = req.file.path.replace(/\\/g, "/");
  let newAddress = req.body.address;
  let newMonthlycstoresales = req.body.monthlycstoresales;
  let newOperator = req.body.operator;
  let newTopsku = req.body.topsku;

  

	const schema = Joi.object().keys({
		// ID: Joi.number().integer().required(),
		 newStation: Joi.string().alphanum().min(1).max(30).required(),
		 newImage: Joi.any().required(),
		 newAddress: Joi.string().alphanum().min(1).max(50).required(),
		 newMonthlycstoresales: Joi.number().integer().required(),
		 newOperator: Joi.string().alphanum().min(1).max(30).required(),
		 newTopsku: Joi.string().alphanum().min(1).max(30).required(),
		 });
		 //password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
		 //access_token: [Joi.string(), Joi.number()],
		 //birthyear: Joi.number().integer().min(1900).max(2013),
		 
		 //}).with('username', 'birthyear').without('password', 'access_token');

		 let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']: newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };

		 let options = { abortEarly: false };
		 Joi.validate(data, schema, options, (err,value) => {
				 if(err) {
						 res.send('An error has occurred: ' + err);
				 }
				});
						  Store.create(data)
              .then(stores => {
                 console.log('Station record was successfully created: ' + stores);
              })
              //.catch(err => {
                // res.send('error: ' + err)
            	return err;
						});               

module.exports = router;

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Hi,

it's look like you are not using expressjs/multer correctly

The init of multer needs to be outside of your middleware code.
Multer own middleware  needs to be execute before yours.

When using array, there are multiple files being uploaded with same param name, so use req.files (with a s) , not req.file.
Omar MartinBridgemaker

Author

Commented:
Please arrange it for me or give an example....I would greatly appreciate it.
leakim971Multitechnician
Top Expert 2014

Commented:
replace :
let newImage = req.file.path.replace(/\\/g, "/");
by :
let newImage = req.file  && req.file.path.replace(/\\/g, "/");
Ensure you’re charging the right price for your IT

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Omar MartinBridgemaker

Author

Commented:
Did it work on your end Leakim971?
leakim971Multitechnician
Top Expert 2014

Commented:
did not tried, but the error :"TypeError: Cannot read property 'path' of undefined"
is from here when req.file is null
let me know if you still have the error with the following :

router.post("/station",upload.single("image"), (req, res, next) => {
    let newStation = req.body.station;
   let newImage = null;
    if(req.file && req.file.path) {
        newImage = req.file.path.replace(/\\/g, "/");
    }

    let newAddress = req.body.address;
    let newMonthlycstoresales = req.body.monthlycstoresales;
    let newOperator = req.body.operator;
    let newTopsku = req.body.topsku;
Omar MartinBridgemaker

Author

Commented:
Leakim971,

It worked. I am not getting any error, however, there is no Joi error message when a field is left out or is incorrect. So the validation is not working yet.

const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");


//Setting Storage Engine

const storage = multer.diskStorage({
	destination: function (req, file, cb) {
    cb(null, "uploads/");
	},
	filename: function (req, file, cb) {
			cb(
			null,
			`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
		);
  }
});

const fileFilter = function (req, file, cb) {
	if (
		file.mimetype === "image/jpeg" ||
		file.mimetype === "image/png" ||
		file.mimetype === "image/gif"
	) {
		cb(null, true);
	} else {
		cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
	}
};

const upload = multer({
	storage: storage,
	limits: {
		fileSize: 1024 * 1024 * 5
	},
	fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////

router.post("/station",upload.single("image"), (req, res, next) => {
	let newStation = req.body.station;
 let newImage = null;
	if(req.file && req.file.path) {
			newImage = req.file.path.replace(/\\/g, "/");
	}
	let newAddress = req.body.address;
	let newMonthlycstoresales = req.body.monthlycstoresales;
	let newOperator = req.body.operator;
	let newTopsku = req.body.topsku;

  

	const schema = Joi.object().keys({
		// ID: Joi.number().integer().required(),
		 newStation: Joi.string().alphanum().min(1).max(30).required(),
		 newImage: Joi.any().required(),
		 newAddress: Joi.string().alphanum().min(1).max(50).required(),
		 newMonthlycstoresales: Joi.number().integer().required(),
		 newOperator: Joi.string().alphanum().min(1).max(30).required(),
		 newTopsku: Joi.string().alphanum().min(1).max(30).required(),
		 });
		 //password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
		 //access_token: [Joi.string(), Joi.number()],
		 //birthyear: Joi.number().integer().min(1900).max(2013),
		 
		 //}).with('username', 'birthyear').without('password', 'access_token');

		 let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']:newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };

		 let options = { abortEarly: false };
		 Joi.validate(data, schema, options, (err,value) => {
				 if(err) {
						 res.send('An error has occurred: ' + err);
				 }
				});
						  Store.create(data)
              .then(stores => {
                 console.log('Station record was successfully created: ' + stores);
              })
              //.catch(err => {
                //res.send('error: ' + err)
            	//return err;
						});               

module.exports = router;

Open in new window

leakim971Multitechnician
Top Expert 2014

Commented:
why your data have different names than the name in the schema?
Omar MartinBridgemaker

Author

Commented:
I corrected it as you pointed out and still the validation does not work.....all the fields are uploaded as is...

const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");


//Setting Storage Engine

const storage = multer.diskStorage({
	destination: function (req, file, cb) {
    cb(null, "uploads/");
	},
	filename: function (req, file, cb) {
			cb(
			null,
			`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
		);
  }
});

const fileFilter = function (req, file, cb) {
	if (
		file.mimetype === "image/jpeg" ||
		file.mimetype === "image/png" ||
		file.mimetype === "image/gif"
	) {
		cb(null, true);
	} else {
		cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
	}
};

const upload = multer({
	storage: storage,
	limits: {
		fileSize: 1024 * 1024 * 5
	},
	fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////

router.post("/station",upload.single("image"), (req, res, next) => {
	let newStation = req.body.station;
 let newImage = null;
	if(req.file && req.file.path) {
			newImage = req.file.path.replace(/\\/g, "/");
	}
	let newAddress = req.body.address;
	let newMonthlycstoresales = req.body.monthlycstoresales;
	let newOperator = req.body.operator;
	let newTopsku = req.body.topsku;

  

	const schema = Joi.object().keys({
		// ID: Joi.number().integer().required(),
		 Station: Joi.string().alphanum().min(1).max(30).required(),
		 Image: Joi.any().required(),
		 Address: Joi.string().alphanum().min(1).max(50).required(),
		 ['Monthly C-Store Sales']: Joi.number().integer().required(),
		 Operator: Joi.string().alphanum().min(1).max(30).required(),
		 ['Top SKU']: Joi.string().alphanum().min(1).max(30).required(),
		 });
		 //password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
		 //access_token: [Joi.string(), Joi.number()],
		 //birthyear: Joi.number().integer().min(1900).max(2013),
		 
		 //}).with('username', 'birthyear').without('password', 'access_token');

		 let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']:newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };

		  //let data = {newStation: newStation, newImage: newImage, newAddress: newAddress, newMonthlycstoresales:newMonthlycstoresales, newOperator: newOperator, newTopsku: newTopsku };

		 let options = { abortEarly: false };
		 Joi.validate(data, schema, options, (err,value) => {
				 if(err) {
						 res.send('An error has occurred: ' + err);
				 }
				});
						  Store.create(data)
              .then(stores => {
                 console.log('Station record was successfully created: ' + stores);
              })
              //.catch(err => {
                // res.send('error: ' + err)
            	//return err;
						});               

module.exports = router;

Open in new window

leakim971Multitechnician
Top Expert 2014

Commented:
what about :

const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");


//Setting Storage Engine

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, "uploads/");
    },
    filename: function (req, file, cb) {
        cb(
            null,
            `${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
            );
    }
});

const fileFilter = function (req, file, cb) {
    if (
        file.mimetype === "image/jpeg" ||
        file.mimetype === "image/png" ||
        file.mimetype === "image/gif"
    ) {
        cb(null, true);
    } else {
        cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
    }
};

const upload = multer({
    storage: storage,
    limits: {
        fileSize: 1024 * 1024 * 5
    },
    fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////

router.post("/station",upload.single("image"), (req, res, next) => {
    let Station = req.body.station;
    let Image = null;
    if(req.file && req.file.path) {
        Image = req.file.path.replace(/\\/g, "/");
    }
    let Address = req.body.address;
    let Monthlycstoresales = req.body.monthlycstoresales;
    let Operator = req.body.operator;
    let Topsku = req.body.topsku;



    const schema = Joi.object().keys({
        // ID: Joi.number().integer().required(),
        Station: Joi.string().alphanum().min(1).max(30).required(),
        Image: Joi.any().required(),
        Address: Joi.string().alphanum().min(1).max(50).required(),
        Monthlycstoresales: Joi.number().integer().required(),
        Operator: Joi.string().alphanum().min(1).max(30).required(),
        Topsku: Joi.string().alphanum().min(1).max(30).required(),
    });
    //password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
    //access_token: [Joi.string(), Joi.number()],
    //birthyear: Joi.number().integer().min(1900).max(2013),

    //}).with('username', 'birthyear').without('password', 'access_token');

    let data = { Station, Image, Address, Monthlycstoresales , Operator, Topsku };

    //let data = {newStation: newStation, newImage: newImage, newAddress: newAddress, newMonthlycstoresales:newMonthlycstoresales, newOperator: newOperator, newTopsku: newTopsku };

    let options = { abortEarly: false };
    Joi.validate(data, schema, options, (err,value) => {
        if(err) {
            res.send('An error has occurred: ' + err);
        }
    });
    Store.create(data)
        .then(stores => {
            console.log('Station record was successfully created: ' + stores);
        })
    //.catch(err => {
    // res.send('error: ' + err)
    //return err;
});

module.exports = router;

Open in new window

Omar MartinBridgemaker

Author

Commented:
Same result. No error but no validation. Perhaps, it is because the formData receives the data as one big object but nothing happens.
Bridgemaker
Commented:
 const result = Joi.validate(data, schema, options);
        //if(err) {
					// If result.error === null, payload is valid
					console.log(`The validation error is: ${result.error}`);
            res.send(`The validation error is: ${result.error}`);
        //}
		
		if(result.error === null) {
    Store.create(data)
        .then(stores => {
						console.log('Station record was successfully created: ' + stores);
                        res.send('Station record was successfully created: ' + stores);
                        res.redirect('/alldata');
                        //or res.redirect('api/alldata');
				})
		}
    //.catch(err => {
    // res.send('error: ' + err)
    //return err;
});

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial