简体   繁体   中英

How can I return Multer Error to Client while using Express-Validator?

Updated Post: Scroll to bottom of post for updated info


Original Post

I need some help on this one. I'm creating a route that takes FormData, validates the file data via Multer (images in this case), then validates the string data using Express-Validator . I've created a working route that completes both validations, but I can't figure out how to take any errors from Multer and return them to the client.

I have set Multer before Express-Validator , so that the req.body can be read by Express-Validator . With that, I can't figure out how to (or if I'm able at all) to pass in the Multer errors for sending back in the response.

My example below should include everything needed for examination, but if you need additional information, please let me know.

 const multer = require('multer') const { check, validationResult } = require('express-validator/check'); const { sanitizeBody } = require('express-validator/filter'); const imageUpload = multer({ dest: 'uploads/', limits: { fileSize: 1000000 }, fileFilter: function (req, file, cb) { let filetypes = /jpeg|jpg/; let mimetype = filetypes.test(file.mimetype); let extname = filetypes.test(path.extname(file.originalname).toLowerCase()); if (mimetype && extname) { return cb(null, true); } cb(new Error('Invalid IMAGE Type')) } }).fields([{ name: 'cover_image', maxCount: 1 }, { name: 'more_images', maxCount: 2 } ]) const validationChecks = [ check('street', 'Invalid Street Name').matches(/^[a-z0-9 ]+$/i).isLength({ min: 1, max: 25 }).trim().escape(), check('city', 'Invalid City Name').matches(/^[az ]+$/i).isLength({ min: 1, max: 15 }).trim().escape() ] router.post('/addnewproperty', imageUpload, validationChecks,(req, res, next) => { const errors = validationResult(req); if (.errors.isEmpty()) { console;log('text validation FAILED'). return res.status(400):json({ errors. errors;array() }). } console;log('validation PASSED'); })

Update 2/6/19

Okay, I think I have found a solution , although not what I expected.

By using the next() function within express, I'm able to utilize Multer in the first route handler where I can receive and send back Multer errors in the response. If no errors arise in this first route handler, I can call next() , to then move along to the next route handler for utilizing express-validator where I can check for and send any errors that arise from string validation.

The code below is a working example of what I'm describing. Not sure if this is acceptable code, but it is working upon some light testing. Any opinions or recommendations on this are welcome in the comments below.


// Here's the meat of what I changed.  
// The config and variables set in the previous code are the same. 

router.post('/addnewproperty',(req, res, next) => {
    imageUpload(req,res,(err)=>{
        if(err){
            console.log(err.message);
            return res.status(400).json(err.message)
        }
        next()
    })
})

router.post('/addnewproperty',validationChecks,(req,res)=>{
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({
            errors: errors.array()
        });
    }
    return res.sendStatus(200)
})

I'll leave this question open in case someone has a better solution of obtaining what I originally set out to do besides the code above.

I have been using a function that creates a Multer middleware that can be placed anywhere in the middleware chain. After it, you can use req.body without the other binary fields.

import { Router } from 'express';
import multer from 'multer';

function makeMulterUploadMiddleware(multerUploadFunction) {
    return (req, res, next) =>
        multerUploadFunction(req, res, err => {
            // handle Multer error
            if (err && err.name && err.name === 'MulterError') {
                return res.status(500).send({
                    error: err.name,
                    message: `File upload error: ${err.message}`,
                });
            }
            // handle other errors
            if (err) {
                return res.status(500).send({
                    error: 'FILE UPLOAD ERROR',
                    message: `Something wrong ocurred when trying to upload the file`,
                });
            }

            next();
        });
}

const upload = multer({ dest: 'uploads/' });

const multerUploadMiddleware = makeMulterUploadMiddleware(upload.single('image'));

const someRouter = Router();

someRouter.post('', multerUploadMiddleware, (req, res) => {
    // now body contains all the fields except the one with the file
    res.send(req.body);
});

export { someRouter };

I'm handling the req.body with the @hapi/joi npm package, but this should work with other validators.

note: The reason I'm not using err instanceof multer.MulterError to check if its a Multer error as described in the Multer docs ( https://github.com/expressjs/multer ) is because of some typescript type-checking errors.

You can get the error by calling the imageUpload middleware directly instead of using it in a middleware chain like in your code.

Non-tested snippet, but hopefully will at least nudge you in the right direction:

router.post('/addnewproperty', validationChecks, (req, res, next) => {  
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
        console.log('text validation FAILED');
        return res.status(400).json({
            errors: errors.array()
        });
    }

    imageUpload(req, res, (multerErr) => {
        if(multerErr){
            console.log('Multer validation FAILED');
            return res.status(400).json({
                errors: [multerErr.message]
            });
        }else{
            console.log('validation PASSED');
        }  
    });
})

For more on the subject, here are the official Multer docs on Error handling .

Hey i will leave you with some snippets which worked for me ,

  • Multer without using file

     const express = require('express'); const multer = require('multer'); const router = express.Router(); const { check, validationResult } = require('express-validator'); var upload = multer(); var tagCreateValidator = [ check('name', 'Name is required') .not() .isEmpty() ]; // @route POST api/tags // @desc Create Tag // @access Public router.post('/', upload.any(), tagCreateValidator, (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } res.send(req.body); });
  • this one is with file although i am not using express validator right now, i will update this answer once this is done

    const express = require('express'); const Multer = require('multer'); const gcsMiddlewares = require('../../gcs_middleware'); const router = express.Router(); const multer = Multer({ storage: Multer.storage, limits: { fileSize: 10 * 1024 * 1024 //Maximum file size is 10MB } }); // @route POST api/users // @desc Register user // @access Public router.post( '/', multer.single('avatar'), gcsMiddlewares.sendUploadToGCS, (req, res, next) => { var imageUrl; if (req.file && req.file.gcsUrl) { imageUrl = req.file.gcsUrl; } var responseBody = { file: imageUrl, body: req.body }; res.send(req); } ); module.exports = router;

I ran into this problem today except I am not using a router. I wanted to be able to return a JSON response of the error when the fileType validation failed. I thought I would share my solution as it may help somebody else. My solution was to add a fourth parameter to the app.post() function call like so:

app.post("/post", fileUpload.single('file'), function(req, res) { 
    //do some stuff after a successful upload.
}, 
function(err, req, res, next) {
    //File upload encountered an error as returned by multer
    res.status(400).json({error: err.message});
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM