【问题标题】:Error handling with Node.js, Async and Formidable使用 Node.js、Async 和 Formidable 处理错误
【发布时间】:2018-03-11 12:12:29
【问题描述】:

在下面的 sn-p 中,我想验证第一个异步方法中的字段。

如果它们无效,我想立即向用户返回一个错误。

我该怎么做?

var form = new formidable.IncomingForm();

async1.series([
    function (callback) {
        form.parse(req);

        form.on('field', function (name, val) {
            // Get the fields
        });

        form.on('fileBegin', function (name, file) {
            if (file.name !== "") {
                file.path = __dirname + '/upload/' + file.name;
            }
        });
        callback();
    },
    function (callback) {
        form.on('file', function (name, file) {
            try {
                // Do something with the file using the fields retrieved from first async method
            }
            catch (err) {
                logger.info(err);
            }
        });


        callback();
    }
], function (err) {
    //the upload failed, there is nothing we can do, send a 500

    if (err === "uploadFailed") {
        return res.send(500);
    }

    if (err) {
        throw err;
    }
    return res.status(200);

});

【问题讨论】:

  • 您可以立即从您正在检查字段的 if 块返回带有错误 return callback(err) 的回调,该回调将直接执行您发送 response codecallback handler function

标签: javascript node.js async.js formidable


【解决方案1】:

我会将表单检查提取到一个函数中:

var form = new formidable.IncomingForm();

function check(name, cb, err) {
 return new Promise((res,rej) => {
  form.on('field', function(n, val) {
        if(n !== name) return;
        if(cb(val)){
          res(val);
        }else{
          rej(err);
       }
   });
 });
}

form.parse(req);

所以现在我们可以实现检查并使用 Promise.all 来总结它们:

 Promise.all(
   check("username", val => val.length > 4, "username isnt valid"),
   check("password", val => true, "we need a password")
 ).then(_ => res.json({status:200}))
  .catch(err => res.json({err}));

如果不是所有的参数都已传递,这将无休止地等待。因此,如果它已结束,让我们终止:

const ended = new Promise((_,rej) => form.on("end", () => rej("params required"));

Promise.race(
 ended,
  Promise.all(
   check("username", val => val.length > 4, "username isnt valid"),
   check("password", val => true, "we need a password")
  )
).then(_ => res.json({status:200}))
 .catch(err => res.json({err}));

因此,我们可以创建良好的数据流。例如:

const login = Promise.all(
  //usable as one liners
 check("username", val => val.length >= 8, "username invalid"),
 //or more extensible
 check("password", val => {
   if( val.length < 8 ) return false;
   //other checks
   console.log(password);
   return true;
 }, "password invalid")
//the field values are resolved by the promises so we can summarize them below 
).then(([username,password]) =>
   //a random (maybe async) call to evaluate the credentials
  checkAgainstDB(username,password)
  //we can directly fail here, err is  "password invalid" or "username invalid"
).catch(err => res.json({error:"login failed",details:err}));

 //another parameter can be extra handled    
const data = check("something", val => val.length);

//we need to summarize all the possible paths (login /data in this case) to one that generates the result
Promise.race(
 //here we join them together
 Promise.all(login, data)
   .then((l, d) => res.json(whatever),
 //and we use the ended promise ( from above ) to end the whole thing
 ended
  //and at last the errors that can occur if the response ended or that have not been canceled early
).catch(e => res.json(e));

【讨论】:

    【解决方案2】:

    var form = new formidable.IncomingForm();
    
    async1.series([
        function (callback) {
            form.parse(req);
    
            form.on('field', function (name, val) {
                if (!name || !val) {
                  // the moment callback is called with an error, async will stop execution of any of the steps
                  // in the series and execute the function provided as the last argument
                  // idimoatic node, when calling the callback with instance of Error
                  return callback(new Error('InvalidParams'));
                }
    
                /**
                 * This is from async documentation: https://caolan.github.io/async/docs.html#series
                 * Run the functions in the tasks collection in series, each one running once the previous function 
                 * has completed. If any functions in the series pass an error to its callback, no more functions are 
                 * run, and callback is immediately called with the value of the error. Otherwise, callback receives 
                 * an array of results when tasks have completed.
                 */
            });
    
            form.on('fileBegin', function (name, file) {
                if (file.name !== "") {
                    file.path = __dirname + '/upload/' + file.name;
                }
            });
    
            form.on('end', function () {
              // call callback with null to specify there's no error
              // if there are some results, call it like callback(null, results);
              return callback(null);
            });
    
            // if you call the callback immediately after registering event handlers for on('field') etc,
            // there will be no time for those events to be triggered, by that time, this function will be 
            // done executing.
            //callback();
        },
        function (callback) {
            form.on('file', function (name, file) {
                try {
                    // Do something with the file using the fields retrieved from first async method
                }
                catch (err) {
                    logger.info(err);
                    return callback(err);
                }
            });
    
            // This should also not be called immediately
            //callback();
        }
    ], function (err) {
        //the upload failed, there is nothing we can do, send a 500
    
        if (err === "uploadFailed") {
            return res.send(500);
        }
    
        if (err.message === 'InvalidParams') {
          // This will be immediately returned to the user.
          return res.sendStatus(400);
        }
    
        if (err) {
          // I'm not sure if this was just for the example, but if not, you should not be throwing an error
          // at run time. 
            throw err;
        }
        return res.status(200);
    
    });

    我在代码中添加了一些 cmets,我需要显示在何处以及如何创建错误以及如何立即将其冒泡给用户。

    参考:Async Documentation

    附:代码片段不可运行,但它具有更好的代码表示。

    -- 编辑--

    从评论中了解更多后,添加另一个sn-p。您不合理地混合了回调和事件处理。您可以只将回调传递给 form.parse 并在收集所有字段时调用回调。您可以进行验证、立即返回错误或直接处理表单字段。

    form.parse(req, function(err, fields, files) {
      if (err) return res.sendStatus(400);
      if (fields.areNotValid()) return res.sendStatus(400);
      // parse fields
    });
    

    或者,您可以为其注册事件处理程序。流入的所有事件都将同时处理,例如 async.series。

    var form = new formidable.IncomingForm();
    
    form.parse(req);
    form.on('field', (name, val) => {
      if (!name || val) {
        console.log('InvalidParams')
        return res.sendStatus(400);
      }
    });
    form.on('fileBegin', (name, file) => {
      if (file.name !== "") {
        file.path = __dirname + '/upload/' + file.name;
      }
    });
    form.on('file', (name, file) => {
    
    });
    form.on('error', (err) => {
      console.log('ParsingError');
      return res.sendStatus(400);
    })
    form.on('end', () => {
      if (res.headersSent) {
        console.log('Response sent already')
      } else {
        // handle what you want to handle at the end of the form when all task in series are finished
        return res.sendStatus(200);
      }
    });

    【讨论】:

    • 这会在我们返回错误时导致 callback() is already called 错误。 form.on('end') 中的回调抛出错误
    • @Robben_Ford_Fan_boy 明白了!我现在更好地理解了这个问题并更新了我的答案。请检查修改,如果有帮助,请告诉我。
    【解决方案3】:

    我假设这是一个验证的好地方,因为这是字段进入的时候:

    form.on('field', function (name, val) {
        //if values are null
        if (!name || !val) {
            //pass the callback an error 
            return callback("Values are null")
        }
        // Get the fields
    });
    

    如果这有帮助,请告诉我。

    【讨论】:

    • 错误被抛出到哪里?有没有办法将错误返回给 API 调用?
    • 在我的代码中,我相信它会通过错误参数发送到异步系列中的最后一个函数。要将其发送到 API 调用(我假设类似 Express),也许您可​​以执行 'return res.send('Values are null')
    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 2018-04-13
    • 1970-01-01
    • 2016-11-05
    • 2018-09-03
    • 2020-01-28
    • 2017-12-12
    • 1970-01-01
    相关资源
    最近更新 更多