I am submitting a form with an image. Using the below code.
router.post("/", upload.upload('image').single('categoryLogo'), categoryRules.categoryCreationRules(), validate, categoryController.createCategory);
It is working fine, but is some validation comes then still image is saving in disk.
so what I tried is :
router.post("/", categoryRules.categoryCreationRules(), validate,upload.upload('image').single('categoryLogo'), categoryController.createCategory);
But in this express validator getting blank body so it throws validation error very time.
What should I do for it, I search on google but I did not found any helpful info I am new in the node.
Rules code:
const categoryCreationRules = () => {
return [
check('name')
.isLength({ min: 1 })
.trim()
.withMessage("Category name is required."),
check('name').custom((name)=>{
return CategoryModel.findOne({name: name}).collation({locale:'en',strength: 2})
.then(category=>{
if(category){
return Promise.reject(category.name+" category already exsist.");
}
})
}),
check('name')
.isLength({max: 100})
.trim()
.withMessage("Category name should not exceed more then 100 characters."),
check('description')
.isLength({max: 255})
.trim()
.withMessage("Category description should not exceed more then 255 characters.")
];
}
In theory, running categoryCreationRules and validate middlewares before multer would be enough. Therefore, you would only need a verification in the request body and if it contains any errors, you just return a bad request response, not letting the request pass to the next middleware (in this case the multer).
A simple example what i'm talking about: (Just to let it clear, the below code won't work)
router.post("/", categoryRules.categoryCreationRules(), validate, upload.upload('image').single('categoryLogo'), categoryController.createCategory);
const validator = (req, res, next) => {
try {
validationResult(req).throw();
// Continue to next middleware, in this case, multer.
next();
} catch (errors) {
// return bad request
res.status(400).send(errors);
}
};
this won´t work because your req.body will be undefined, as you are sending the data as a multipart/form-data (generally used to upload files). And in this case, errors will always be true.
But with multer this will not happen - you will be able to access body fields like description and name and then do the validation code.
This occurs because multer, internally, parses the multipart/form-data request to body with a library called busboy, and with this you can access fields through req.body.
Because of that, i think the best approach here is call multer middleware before your validations middlewares:
router.post("/", upload.upload('image').single('categoryLogo'), categoryRules.categoryCreationRules(), validate, categoryController.createCategory);
And after that, if the validation has an error, you delete the file created from multer and return a bad request response, something like that:
const fs = require("fs");
const validator = (req, res, next) => {
try {
validationResult(req).throw();
// continue to next middleware
next();
} catch (errors) {
fs.unlink(req.file.path, (err) => {
if (err) {multipart/form-data
/* HANLDE ERROR */
}
console.log(`successfully deleted ${req.file.path}`);
});
// return bad request
res.status(400).send(errors);
}
};
You can get more info about this in the below links:
node-js-with-express-bodyparser-unable-to-obtain-form-data-from-post-request
req-body-undefined-multipart
html-multipart-form-data-error-in-req-body-using-node-express
To upload an image , you have set the enctype of form to multipart/form-data . But if you use multer later, you don't have the form data parsed, hence probaby giving undefined.
Please check multiparty , an npm module
https://www.npmjs.com/package/multiparty
It also parses other fields along with file uploads and validation might be easy to set.
Related
I'm trying to work up a proof of concept wherein I receive a POST request that then triggers a different POST request. I thought I would setup a nodejs/express server to test this, but the POST request I'm receiving has Content-Type application/json and a body with data that is (I believe) incorrectly formatted JSON.
e.g. { something: "data" }
I can't change this request, and I just need to use the data to send a different request, but Node throws an error: "Unexpected token in json at position" everytime it receives the request. I'm sure this is because the content is marked as JSON, but the body isn't formatted correctly; I just don't know enough about node/express to know how to deal with it. I looked at bodyParser and writing my own middleware function, but I could really use some direction.
Following DamiToma's advice I added error handling, but for some reason the middleware isn't being called:
const express = require('express');
const router = express.Router();
module.exports = router;
router.use((error, req, res, next) => {
console.log("Error handling middleware called...");
if(error instanceof SyntaxError) {
switch(error.type) {
case 'entity.parse.failed':
// JSON is incorrect
console.log('JSON is incorrect');
break;
default:
// other error
break;
}
res.status(400).end();
} else {
next();
}
});
// POST
router.post('/post', (req, res) => {
res.send('POST');
console.log(req.body);
});
If I put the error handling on the app rather than router, it works, I'm just not sure why it doesn't apply when I put in on the router.
Using express, you can setup a middleware to catch this kind of errors
router.use((error, req, res, next) => {
if(error instanceof SyntaxError) {
switch(error.type) {
case 'entity.parse.failed':
// JSON is incorrect
break;
default:
// Some other kind of error happened
break;
}
res.status(400).end();
} else {
next();
}
})
I have been looking through multiple tutorials and stack overflow questions but for some reason I just cannot make this work. I have issues with uploading a file, so maybe fixing that first would solve the whole issue.
I tried a few options of sending a file from the front end to the back end, but it seems to always "get lost" before reaching the back end.
I have decided to use multer at the NodeJS backend to upload the file. Not sure if I am calling multer upload single right or not. Currently this is the code which I have for it:
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploadedImages',
filename: function(req,file,cb){
cb(null,file.originalname)
}
}) ;
const upload = multer({storage: storage})
exports.saveDrawing = async(req, res, next) => {
try
{
//save image
//tried a few different logs, but with FormData it seems like everything always empty
console.log("Image:");
console.log(req.body.drawingElement);
console.log(req.file);
upload.single('body.file');
return res.status(200).json({message: element});
}
}
catch (err)
{
console.log("Error at drawing save: " + err)
return res.status(500).json({message: "Error - Could not add/edit Drawing"});
}
}
And this is how it is sent from the Angular front end:
setDrawing(params, image): Observable<any> {
const formData = new FormData();
formData.append('file', image)
formData.append('data', params)
console.log("File: ");
console.log(formData.get('file'));
console.log("Data: ");
console.log(formData.get('data'));
return this.http.post<any>(`api/v1/structure/drawing/save`, formData);
}
At this stage printing out the data shows the right values. And the browser shows the right payload too:
At the back end I cannot see them in the req, req.body is empty, there is no req.form. For this api call before I have tried to include any files without the FromData I have accessed the data from req.body.
Am I looking for the data at the right place?
You're not using multer correctly, it's not doing anything.
To implement it as a middleware which you call from your handler, check the example from the docs
So, your handler should look something like this:
// setup multer middleware, set file field name
const upload = multer({storage: storage}).single('file');
exports.saveDrawing = async(req, res, next) => {
// now use the middleware, handle errors
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
return res.status(500).json({message: "Error - Could not add/edit Drawing"});
} else if (err) {
// An unknown error occurred when uploading.
return res.status(500).json({message: "Error - Could not add/edit Drawing"});
}
// Everything went fine.
console.log("Image:");
console.log(req.body.drawingElement);
console.log(req.file);
return res.status(200).json({message: element});
});
});
Current Situation
In the front-end side of the application I have a simple form, from where I can get 3 parameters for the data before uploading it to the back-end:
The form submition function
e.preventDefault();
const data = new FormData(e.currentTarget) //contains a string key called title
data.append("file", JSON.stringify(file)) //file object
data.append("description", JSON.stringify(editorState)) //description object
handleSubmit(e, data)
To prevent any error, I have some client-side validations (cannot upload if some parameters are empty). This is how I fetch the data:
handleSubmit
await fetch(process.env.NEXT_PUBLIC_DR_HOST, {
method: 'POST',
body: body, //body is the data argument
}).then(res =>
{
// window.location = res.url;
})
After that, the data will be sent to the server:
The server
router.post('/', upload.single('file'), validateDbData, tryAsync(async (req, res, next) =>
And using the multer middleware(upload.single('file')), the file will be uploaded respectively (storage).
The Problem
As you can see, after the upload.single('file') middleware, comes another one called validateDbData. This is the code for it:
const declarationSchema = Joi.object({
title: Joi.string().required(),
description: Joi.object().required(),
file: Joi.object()
})
const { error } = declarationSchema.validate(
req.body
)
console.log(error)
if (error)
{
const msg = error.details.map(e => e.message).join(',') //
throw new ServerError("Invalid Data", 400)
}
else
{
next()
}
And if there is something wrong (say the title is empty) an error is thrown, so the server-side validation works. But, the upload.single('file') middleware is called before the validateDbData validation midleware. This means that even if there is an error and everything got canceled, the file remained uploaded in the cloud (storage).
Question
How can I validate the data inside the FormData on the server before uploading the file parameter to the cloud (storage)? The multer middleware accepts only multipart/form-data, so reversing the middleware order will not work.
Potential solution: express-fileupload
file remained uploaded in the cloud.
What is meant by cloud here? Multer lets you store the file onto a storage or on memory, and then subsequent middleware calls get the file and its info in the req.file field.
Your validator validateDbData requires the presence of the file, which leaves you with the option of housekeeping(to be performed in validateDbData middleware) if the form is not valid, depending on the use-case you should carefully chose where multer middleware stores the file, on memory or on a storage.
For some reason, request body is undefined when trying to make a post request:
here is my router:
router.route("/").post(schoolController.createSchool);
here is what I put in schoolController for createSchool:
exports.createSchool = async (req, res) => {
try {
console.log(req.body);
// return undefined
const newSchool = await School.create(req.body);
res.status(201).json({
status: "success",
data: {
school: newSchool,
},
});
} catch (err) {
res.status(400).json({
status: "fail",
message: err,
});
}
};
adding on, I am following jonas's nodejs course on udemy, and he has almost the exact thing as this, except its for handling tour requests instead of school
The problem you are facing here is likely that you have not configured the body-parser middleware correctly. The normal req will not contain any property by the name body. Only once the request passes through the body-parser middleware, the body key will be added to the req. You can try console logging req. If the request is logged correctly, it is more than likely that you need to look into configuring you bodyparser middleware correctly before you can use req.body in your code.
I send JSON data to route:
const data=[{name:this.name}]
//qs.stringify(payload)
axios
.post('/users',{name:this.name})
.then(
response => {
console.log(response.data);
}
)
.catch(
// error=>console.log(error)
)
And try get data:
router.post('/', function (req, res, next) {
var data = req.body; // here is your data
var obj=JSON.parse(data);
console.log(obj.toString());
res.toString("ok");
});
And I got error 500.
Why not get the data?
Your client side code is fine, except that the constant named data is entirely unused. On server-side however, req.body almost certainly contains a parsed JSON object (provided you have included a body paring middleware already). If you haven't included any body parsing middleware then req.body would be undefined.
Additionally, the toString() method in res doesn't send any response, it simply returns a string representation of the response.
You would need to make following changes to your code:
Include a body parsing middleware (eg. body-parser) to your express middleware chain, if one isn't already included.
Don't call JSON.parse(req.body). body-parser would already have done that. Calling it again will only throw exception, and return 500.
To convert an Object to JSON string, use JSON.stringify() method. obj.toString() will probably only return [object Object].
Send response using one of .send(), .json(), .end() methods on res. Since you need to send a string back, res.send("ok") seems most appropriate.
The changed code should appear something like this:
router.post('/', function (req, res, next) {
var data = req.body; // here is your data
console.log(JSON.stringify(data));
res.send("ok");
});