Express-validator 在 multer 函数后不执行

Express-validator not executing after multer function

我的 express 应用程序中有一个表格,供用户输入一些文本,并上传 1-3 张照片。我正在使用 multer 处理文件上传到我的 S3 存储桶,我正在使用 express-validator.

验证表单的其余部分

这使我的 POST 路线看起来像:

router.post("/list-product", listLimiter, function (req, res) {
  singleUpload(req, res, function (err) {
    // if any multer errors, redirect to form
    if (err) {
      res.redirect(
        "list-product" +
          "?error=Image error, please make sure your file is JPG or PNG"
      );
      return;
    }
    // if no multer errors, validate rest of form
  });
});

我在集成 express-validator 时遇到问题。我已经在这个问题上停留了几天了,我想我已经接近了。我下面的代码将捕获 multer 错误,并且它将创建一个新的 Product if 所有输入都已填写。所以发生的事情是我的 express-validator 没有捕捉到这里的错误 if (!errors.isEmpty()) { // handle errors,它只是跳过它并直接进入 else { let product = new Product({。我知道这一点,因为当我将输入留空时,它会抛出缺少模式路径的猫鼬错误。

let upload = require("../controllers/uploadController");
const singleUpload = upload.single("image");


router.post("/list-product", listLimiter, function (req, res, next) {
  singleUpload(req, res, function (err) {
    // if any multer errors, redirect to form
    if (err) {
      res.redirect(
        "list-product" +
          "?error=Image error, please make sure your file is JPG or PNG"
      );
      return;
    }
    // if no multer errors, validate rest of form

    body("productName")
      .trim()
      .isLength({ min: 1 })
      .withMessage("Please enter the name of your product"),
      body("productPrice")
        .isNumeric()
        .withMessage("Please enter a valid price"),
      body("productCategory")
        .trim()
        .isLength({ min: 1 })
        .withMessage("Please select the category of your product"),
      body("productDescription")
        .trim()
        .isLength({ min: 50 })
        .withMessage("Minimum 50 characters")
        .isLength({ max: 500 })
        .withMessage("Maximum 500 characters"),
      body("businessName")
        .trim()
        .isLength({ min: 1 })
        .withMessage("Please enter the name of your business"),
      body("website")
        .trim()
        .isURL()
        .withMessage("Please enter the URL for your product or business");

    check("*").escape();

    const errors = validationResult(req);
    let errArray = errors.array();

    if (!errors.isEmpty()) {
      res.render("list", {
        form: req.body,
        errors: errArray,
        msg: "Please check the form for errors",
        option: req.body.productCategory,
      });
      return;
    } else {
      let product = new Product({
        business: req.body.businessName,
        productName: req.body.productName,
        category: req.body.productCategory,
        price: req.body.productPrice,
        description: req.body.productDescription,
        website: req.body.website,
        imageURL:
          "https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
        imageURL2:
          "https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
      });
      product.save(function (err) {
        if (err) {
          console.log(err);
          return next(err);
        }
        res.redirect("/list-product");
      });
    }
  });
});

我真的很感激任何帮助或建议,因为我已经坚持了几天,感觉真的很愚蠢!..

我将包括最后一个代码块,这是我的 express-validator 函数,它在我验证文本输入时起作用,所以我知道这种方法确实有效。我只是有一个将它与多重功能结合起来真的很艰难

exports.list__post = [
  body("productName")
    .trim()
    .isLength({ min: 1 })
    .withMessage("Please enter the name of your product"),
  body("productPrice")
    .isNumeric()
    .withMessage("Please enter a valid price"),
  body("productCategory")
    .trim()
    .isLength({ min: 1 })
    .withMessage("Please select the category of your product"),
  body("productDescription")
    .trim()
    .isLength({ min: 50 })
    .withMessage("Minimum 50 characters")
    .isLength({ max: 500 })
    .withMessage("Maximum 500 characters"),
  body("businessName")
    .trim()
    .isLength({ min: 1 })
    .withMessage("Please enter the name of your business"),
  body("website")
    .trim()
    .isURL()
    .withMessage("Please enter the URL for your product or business"),

  check("*").escape(),

  (req, res, next) => {
    const errors = validationResult(req);
    let errArray = errors.array();

    if (!errors.isEmpty()) {
      console.log(req.body)
      res.render("list", {
        form: req.body,
        errors: errArray,
        msg: "Please check the form for errors",
        option: req.body.productCategory,
      });
      return;
    } else {
      
      let product = new Product({
        business: req.body.businessName,
        productName: req.body.productName,
        category: req.body.productCategory,
        price: req.body.productPrice,
        description: req.body.productDescription,
        website: req.body.website,
        imageURL: "https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
        imageURL2:"https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
      });
      product.save(function (err) {
        if (err) {
          console.log(err);
          return next(err);
        }
        res.redirect("/list-product");
      });
    }
  },
];

更新:

// uploadController.js

const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
const crypto = require("crypto");
require("dotenv").config();

// config aws
aws.config.update({
  secretAccessKey: process.env.SECRETACCESSKEY,
  accessKeyId: process.env.ACCESSKEYID,
  region: "us-west-2",
});
const s3 = new aws.S3();

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
    cb(null, true);
  } else {
    cb(new Error("Invalid format, only JPG and PNG"), false);
  }
};

const upload = multer({
  fileFilter: fileFilter,
  limits: { fileSize: 1024 * 1024 },
  storage: multerS3({
    s3: s3,
    bucket: "mybucket",
    acl: "public-read",
    metadata: function (req, file, cb) {
      cb(null, { fieldName: file.fieldname });
    },
    key: function (req, file, cb) {
      req.imageName = crypto.randomBytes(16).toString("hex");
      cb(null, req.imageName);
    },
  }),
});

module.exports = upload;

快速修复:

  • 在中间件中调用singleUpload,这里你使用upload.single()上传单个文件,所以如果你想上传多个,你需要从视图文件标签中删除multiple属性文件然后使用 upload.array('field name', total count of files)
router.post("/list-product", 
  singleUpload, 
  • 验证中间件中的其他参数
  • 我注意到您没有在表单中添加 businessName 字段,因此这将 return 出错,您可以在表单中添加它或从此处以及模式中删除验证。
  [
    body("productName").trim().isLength({ min: 1 }).withMessage("Please enter the name of your product"),
    body("productPrice").isNumeric().withMessage("Please enter a valid price"),
    body("productCategory").trim().isLength({ min: 1 }).withMessage("Please select the category of your product"),
    body("productDescription").trim().isLength({ min: 50 }).withMessage("Minimum 50 characters").isLength({ max: 500 }).withMessage("Maximum 500 characters"),
    body("businessName").trim().isLength({ min: 1 }).withMessage("Please enter the name of your business"),
    body("website").trim().isURL().withMessage("Please enter the URL for your product or business"),
    check("*").escape()
  ], 
  • 中间件回调函数,这里req会提供所有body参数和上传的文件对象,
  • 只需控制台 req.body 并使用 req.file 将 return 在 S3 中上传的文件的所有详细信息,包括文件名和位置,我建议您使用文件名和位置来自这个对象,我已经控制台了,
  • 使用我们从 fileFitler
  • 传递的 req.fileTypeInvalid 处理文件扩展名错误
  function (req, res, next) {
    console.log(req.body, req.file);

    // FILE EXTENSION ERROR
    if (req.fileTypeInvalid) {
        res.redirect("list-product" + "?error=" + req.fileTypeInvalid);
        return;
    }

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        let errArray = errors.array();
        let errorsObj = {}; // access errors indiviually 
        errArray.map((item) => {
            const id = item.param;
            delete item.param;
            errorsObj[id] = item;
        });
        res.render("list", {
            form: req.body,
            errors: errorsObj,
            msg: "Please check the form for errors",
            option: req.body.productCategory,
        });
        return;
    }

    let product = new Product({
        business: req.body.businessName,
        productName: req.body.productName,
        category: req.body.productCategory,
        price: req.body.productPrice,
        description: req.body.productDescription,
        website: req.body.website,
        imageURL: "https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
    });
    product.save(function (err) {
        if (err) {
            console.log(err);
            return next(err);
        }
        res.redirect("/list-product");
    });
});

请求的合并最终版本:

router.post("/list-product", 
  singleUpload, 
  [
    body("productName").trim().isLength({ min: 1 }).withMessage("Please enter the name of your product"),
    body("productPrice").isNumeric().withMessage("Please enter a valid price"),
    body("productCategory").trim().isLength({ min: 1 }).withMessage("Please select the category of your product"),
    body("productDescription").trim().isLength({ min: 50 }).withMessage("Minimum 50 characters").isLength({ max: 500 }).withMessage("Maximum 500 characters"),
    body("businessName").trim().isLength({ min: 1 }).withMessage("Please enter the name of your business"),
    body("website").trim().isURL().withMessage("Please enter the URL for your product or business"),
    check("*").escape()
  ], 
  function (req, res, next) {
    console.log(req.body, req.file);

    // FILE EXTENSION ERROR
    if (req.fileTypeInvalid) {
        res.redirect("list-product" + "?error=Image error, please make sure your file is JPG or PNG");
        return;
    }

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        let errArray = errors.array();
        let errorsObj = {}; // access errors indiviually 
        errArray.map((item) => {
            const id = item.param;
            delete item.param;
            errorsObj[id] = item;
        });
        res.render("list", {
            form: req.body,
            errors: errorsObj,
            msg: "Please check the form for errors",
            option: req.body.productCategory,
        });
        return;
    }

    let product = new Product({
        business: req.body.businessName,
        productName: req.body.productName,
        category: req.body.productCategory,
        price: req.body.productPrice,
        description: req.body.productDescription,
        website: req.body.website,
        imageURL: "https://mybucket.s3-us-west-2.amazonaws.com/" + req.imageName,
    });
    product.save(function (err) {
        if (err) {
            console.log(err);
            return next(err);
        }
        res.redirect("/list-product");
    });
});


uloadController.js 文件中的 fileFilter 中更正,在 req.fileTypeInvalid 和 return 中传递错误,在请求中处理,

const fileFilter = (req, file, cb) => {
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
        cb(null, true);
    } else {
        req.fileTypeInvalid = "Invalid format, only JPG and PNG";
        cb(null, false, req.fileTypeInvalid);
    }
};