简体   繁体   English

使用Node.js,Async和Formidable进行错误处理

[英]Error handling with Node.js, Async and Formidable

In the following snippet I would like to validate the fields in the first async method. 在下面的代码片段中,我想验证第一个异步方法中的字段。

If they are not valid I would like to return an error to the user immediately. 如果它们无效,我想立即向用户返回错误。

How do I do that? 我怎么做?

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);

});

I would extract the form checking into a function: 我会将表单检查提取到一个函数中:

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);

So now we can implement the checks and use Promise.all to summarize them: 所以现在我们可以实现检查并使用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}));

If not all all parameters have been passed, this will wait endlessly. 如果并非所有参数都已通过,则会无休止地等待。 So lets terminate if it was ended: 因此,如果结束则终止:

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}));

So with that we can create a good flow of data. 因此,我们可以创建良好的数据流。 eg: 例如:

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));

 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); }); 

I added some comments in the code where I needed to show where and how to create an error and how it's bubbled up to the user immediately. 我在代码中添加了一些注释,我需要在其中显示创建错误的位置和方式,以及如何立即向用户显示错误。

Reference: Async Documentation 参考: 异步文档

PS Code Snippet is not runnable, but it has a better representation of the code. PS Code Snippet不可运行,但它具有更好的代码表示。

-- edit -- - 编辑 -

After knowing more from the comment, adding another snippet. 从评论中了解更多信息后,添加另一个代码段。 You are unreasonably mixing callback and event handling. 你不合理地混合了回调和事件处理。 You can just pass a callback to form.parse and the callback is called when all fiels are collected. 您可以将回调传递给form.parse,并在收集所有fiel时调用回调。 You can do validation, return error immediately or just handle the form fields right away. 您可以进行验证,立即返回错误或立即处理表单字段。

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

Or, you can register event handlers for it. 或者,您可以为其注册事件处理程序。 All events as they flow in will be handled concurrently, like async.series. 流入的所有事件将同时处理,如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); } }); 

I'm assuming that this is a good place to validate since this is when the fields are coming in: 我假设这是一个验证的好地方,因为这是字段进入的时候:

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
});

Please let me know if this helps. 请让我知道这可不可以帮你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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