2017-09-29 4 views
14

Dans l'extrait suivant, je souhaite valider les champs de la première méthode asynchrone.Traitement des erreurs avec Node.js, Async et Formidable

Si elles ne sont pas valides, je voudrais retourner une erreur à l'utilisateur immédiatement.

Comment faire cela?

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

}); 
+0

Vous pouvez retourner le rappel avec une erreur 'rappel de retour (err)' immédiatement à partir du si bloc où vous vérifiez le champ, que le rappel sera directement exécuter votre 'gestionnaire de rappel function' où vous envoyez' code de réponse ». – Zeeshan

Répondre

1

Je suppose que c'est un bon endroit pour valider car il est quand les champs arrivent:

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

S'il vous plaît laissez-moi savoir si cela aide.

+0

Où sont jetées les erreurs? Existe-t-il un moyen de renvoyer l'erreur à l'appel d'API? –

+0

Dans mon code, je crois qu'il serait envoyé à la dernière fonction de la série asynchrone via le paramètre d'erreur. Pour l'envoyer à l'appel de l'API (je suppose quelque chose comme Express), peut-être que vous pourriez faire 'res res.send ('Valeurs are Null') –

3

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

 
});

J'ai ajouté quelques commentaires dans le code où je devais montrer où et comment créer une erreur et comment il est barboter à l'utilisateur immédiatement.

Référence: Async Documentation

post-scriptum L'extrait de code n'est pas exécutable, mais il a une meilleure représentation du code.

- modifier -

Après avoir pris connaissance plus du commentaire, ajoutant un autre extrait. Vous mélangez indûment le rappel et la gestion des événements. Vous pouvez simplement passer un rappel à form.parse et le rappel est appelé lorsque tous les fichiers sont collectés. Vous pouvez effectuer une validation, renvoyer une erreur immédiatement ou simplement gérer les champs du formulaire immédiatement.

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

Ou, vous pouvez enregistrer des gestionnaires d'événements pour cela. Tous les événements tels qu'ils se dérouleront seront traités simultanément, comme 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); 
 
    } 
 
});

+0

Cela provoquera un rappel() est déjà appelé erreur lorsque nous retournons l'erreur. Le rappel dans form.on ('end') génère une erreur –

+0

@Robben_Ford_Fan_boy Gotcha! Je comprends mieux le problème maintenant et mis à jour ma réponse. S'il vous plaît vérifier les modifications et laissez-moi savoir si cela aide. –

5

J'extraire la forme de vérification en fonction:

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

Alors maintenant, nous pouvons mettre en œuvre les contrôles et utiliser Promise.all pour les résumer:

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

Si tous les paramètres n'ont pas été passés, cela sera ayez sans fin. Laissons donc se terminer s'il était terminé:

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

Donc, avec cela, nous pouvons créer un bon flux de données. par exemple:

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