J'ai un async
middleware dans express, parce que je veux utiliser await
à l'intérieur, pour nettoyer mon code.
const express = require('express');
const app = express();
app.use(async(req, res, next) => {
await authenticate(req);
next();
});
app.get('/route', async(req, res) => {
const result = await request('http://example.com');
res.end(result);
});
app.use((err, req, res, next) => {
console.error(err);
res
.status(500)
.end('error');
})
app.listen(8080);
Le problème est que lorsqu'il rejette, il ne va pas dans mon middleware d'erreur, mais si je supprime le mot clé async
et throw
dans un middleware, il le fait.
app.get('/route', (req, res, next) => {
throw new Error('Error');
res.end(result);
});
Je reçois donc UnhandledPromiseRejectionWarning
au lieu de saisir mon middleware de gestion des erreurs, comment puis-je laisser l'erreur se propager et l'exprimer?
Le problème est que lorsqu'il rejette, il ne va pas dans mon middleware d'erreur, mais si je supprime le mot-clé async et le jette dans un middleware, il le fait.
express
ne prend pas en charge les promesses actuellement, une assistance pourrait être fournie dans la future version de [email protected]
Ainsi, lorsque vous transmettez une fonction middleware, express
l'appelle dans un bloc try/catch
.
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
Le problème est que try/catch
N'acceptera pas un rejet Promise
en dehors d'une fonction async
et puisque express
n'ajoute pas de .catch
vers le Promise
renvoyé par votre middleware, vous obtenez un UnhandledPromiseRejectionWarning
.
La manière la plus simple est d'ajouter try/catch
À l'intérieur de votre middleware et d'appeler next(err)
.
app.get('/route', async(req, res, next) => {
try {
const result = await request('http://example.com');
res.end(result);
} catch(err) {
next(err);
}
});
Mais si vous avez beaucoup de middlewares async
, cela peut être un peu répétitif.
Comme j'aime mes middlewares aussi propres que possible et que je laisse généralement les erreurs se multiplier, j'utilise un wrapper autour de async
middlewares, qui appellera next(err)
si la promesse est rejetée, atteignant le gestionnaire d'erreurs express et évitant UnhandledPromiseRejectionWarning
const asyncHandler = fn => (req, res, next) => {
return Promise
.resolve(fn(req, res, next))
.catch(next);
};
module.exports = asyncHandler;
Maintenant, vous pouvez l'appeler comme ceci:
app.use(asyncHandler(async(req, res, next) => {
await authenticate(req);
next();
}));
app.get('/async', asyncHandler(async(req, res) => {
const result = await request('http://example.com');
res.end(result);
}));
// Any rejection will go to the error handler
Il existe également des packages qui peuvent être utilisés
La réponse avec asyncHandler est bonne et utile, mais il n'est toujours pas confortable d'écrire ce wrapper dans chaque route. Je propose de l'améliorer:
const asyncHandler = fn => (req, res, next) => {
return Promise
.resolve(fn(req, res, next))
.catch(next)
}
const methods = [
'get',
'post',
'delete' // & etc.
]
function toAsyncRouter(router) {
for (let key in router) {
if (methods.includes(key)) {
let method = router[key]
router[key] = (path, ...callbacks) => method.call(router, path, ...callbacks.map(cb => asyncHandler(cb)))
}
}
return router
}
et maintenant on peut faire ça:
const router = toAsyncRouter(express().Router())
router.get('/', someAsyncController)
et cetera.
Il y a une minute, un module npm a été ajouté async-express-decorator .
Eh bien, j'ai trouvé cela - https://github.com/davidbanham/express-async-errors/ , alors exigez le script et vous êtes prêt à partir
const express = require('express');
require('express-async-errors');
Vous devez utiliser try-catch et dans la section catch, passez simplement l'erreur dans le paramètre next () Comme ceci -
async create(req, res, next) {
try {
const userProp = req.body;
const user = new User(userProp)
const response = await user.save()
const token = await user.createJWSToken()
res.send({response, token})
} catch (err){
next(err)
}
}
Et évidemment, mettez ce middleware express dans votre fichier index.js.
app.use((err, req, res, next) => {
res.status(422).send({ error: err.message });
});