I'm trying to setup a simple system for rendering user-input-errors and stopping propogation in express, this is what I've got so far:
routingFunction= (req, res, next) {
//setting up a test in express-validator
var test = req.assert('access_token', 'required').notEmpty();
isValid(test, next);
//non error stuff
}
isValid = (tests, next) => {
//some more code here, that checks if any errors were found and save tem to the array errors.
if(errors.length > 0){
return next(new Error());
}
};
//a middleware that catches errors:
app.use((err, req, res, next) => {
res.json('error').end();
});
My problem with this is that it doesn't stop the propagation when I'm calling Next(new Error());, I could return true/false from isValid and then return next(new Error()), but that would add a lot of bloat to my controllers, is there some better way to do it from within the helper function?
In main route file, e.g. routes/index.js
// Set of func(req, res, next)
let v = require('validator'); // middleware-validators
let user = require('user'); // routes for user
...
app.get('/smth-route-of-user', v.isTokenSet, v.isEmail, ..., user.get)
In middlewares/validator.js
let v = require('validator-func-list');
...
exports.isTokenSet = function (req, res, next) {
if (v.isValid(req.body.token))
next(); // Forwarding to next middleware. In our route next() called v.isEmail
else
next(new Error('Token is empty')); // Stop route chain and call error-middleware;
}
exports.isEmail = function (req, res, next) {
...
You can join validators to one, e.g. checkUser(), and use only one in route.
In middlewares/errorHandler.js
module.exports = function (err, req, res, next) {
let msg = err.message; // Here we see 'Token is empty';
if (req.xhr)
res.json(msg);
else
res.render('error_page.html', {message: msg, ...});
// Here we can call next(err) to forwarding error to next errorHandler. In example below it was errorHandler2.
}
In app.js don't forget to attach error-middleware to application.
app.use(require('middlewares/errorHandler'));
app.use(require('middlewares/errorHandler2'));
If you need collect errors then validator must push error to req.errors (or another field as you want) and call next() without error. In render middleware you simple check req.errors.length and show normal or error page.
Code after isValid(test, next); always execute. Code below block it, but imho is dirty.
routingFunction = (req, res, next) {
var test = req.assert('access_token', 'required').notEmpty();
if (!isValid(test, next))
return; // callback called by isValid. It's dust.
//non error stuff
...
next(); // callback must be called
}
isValid = (tests, next) => {
if(errors.length > 0){
next(new Error());
return false;
}
return true;
};
More better use like this
routingFunction = (req, res, next) {
var test = req.assert('access_token', 'required').notEmpty();
if (!isValid(test))
return next (new Error('error description'));
//non error stuff
...
next();
}
Related
I am writing a middleware function that looks for validation errors and if the error is found gives out a certain output else continues the program flow. I have two functions with the exact code but they check for different schemas.
My first function runs without any exception. However, when I try to execute the second function I get an error in the console.
const validateCampground = (req, res, next) => {
const { error } = campgroundSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next();
}
};
const validateReview = (req, res, next) => {
const { error } = reviewSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next(); //this is the point where the exception occurs
}
};
It is only inside the validateReview function where next middleware function is not recognised as a valid function.
The problem was not with the next() middleware but instead it was with the route as I was wrapping the route with the validateReview function.
I was doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview(
catchAsync(async (req, res) => {
//my Logic here
})
));
Whereas , I should have been doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview,
catchAsync(async (req, res) => {
//my logic here
})
);
hi if you want to use a middileware
exports.middileware = (req,res,next)=>{
try{
//middileware logic
next();
}catch(err){
//print the error
})
}
}
and call the exported middileware file in requires file to check the middileware function
const { middileware } = require('path');
and use like this
router.get('/routename',middleware,nextfunction) //router you can choose as you like get,post,patch anything
try this out
I got this error when I omitted "req" and "res" in the function's parameters. When I added them, the error disappeared. Since I was using typescript, the first scenario looked like this:
function traceRoute(next){
console.log(routeTrace);
next();
}
Corrected to:
function traceRoute(req, res, next){
console.log(routeTrace);
next();
}
Question: Using Express how do I redirect a client to an error page when an error is thrown inside an interior function? Logging the error from a function several layers deep is no problem, the redirection is what I don't understand.
Example that works: The following simplified code doesn't use an interior function and successfully redirects the client to an error page when an error is thrown.
app.get('/', function(req, res, next) {
let cat = 10;
try {
if (cat === 10) throw new Error('cat should not be 10');
} catch(error) {
return next(error);
}
res.render('homepage');
}
);
// custom error handler
app.use( function(error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
Uh oh, this one doesn't work: Unfortunately, when an interior function is used the client always gets to res.render('homepage'). In real code the error is almost always going to be several layers deep so there must be a logical solution to this common problem.
app.get('/', function(req, res, next) {
interiorFunction(next);
res.render('homepage');
}
);
function interiorFunction(next) {
let cat = 10;
try {
if (cat === 10) throw new Error('cat should not be 10');
} catch(error) {
return next(error);
}
}
// custom error handler
app.use( function(error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
I interpret interior function as an intermediate middleware.
The way you showed it always hit the res.render('homepage'), also you are not calling next() when the middleware has no error, you can fix it by doing:
app.get('/', interiorFunction, function (req, res, next) {
res.render('homepage');
});
function interiorFunction(req, res, next) {
let cat = 11;
try {
if (cat === 11) throw new Error('cat should not be 10');
next();
} catch (error) {
return next(error);
}
}
EDIT
That was a solution based on middleware, but that seems that its not your case so the alternative is to have some logic to not call res.render('homepage') when there is an error. This is common to both the approaches: they both don't run the res.render('homepage') statement, in the end all resumes to if that code is run or not. So as an very simple working example:
app.get('/', function (req, res, next) {
const ok = interiorFunction(next);
if (ok) {
res.render('homepage');
}
});
function interiorFunction(next) {
let cat = 11;
try {
if (cat === 11) throw new Error('cat should not be 10');
return true;
} catch (error) {
next(error);
return false;
}
}
// custom error handler
app.use(function (error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
EDIT 2:
Or even better:
app.get('/', function (req, res, next) {
try {
interiorFunction();
res.render('homepage');
} catch (error) {
console.log(error);
next(error);
}
});
function interiorFunction() {
let cat = 11;
if (cat === 11) throw new Error('cat should not be 10');
}
So the pattern here is not to pass the next function to interior functions, it don't make sense at all since they don't need to know that next exist, but instead make them throw exceptions and in the top caller, use a catch block to call next(error) and at the same time you're not running the res.render('homepage') line
I am trying to pass data from one middleware to another. The data is then returned to the client in the next middleware. I, however, am unable to catch it in the send.call.
How can I catch the data and send it?
Thank you all in advance.
const myPreMiddleware = async (req, res, next) => {
req.myData = myData;
next();
};
const myPostMiddleware = (req, res, next) => {
let send = res.send;
res.send = async function (body) {
send.call(this, req.myData); // The data i NOT accessible here
};
console.log("req.myData : " + JSON.stringify(req.myData)); // The data is accessible here
next();
};
app.use(myPreMiddleware);
app.use(myPostMiddleware);
Try pass the variable that you want to pass to the next() callback
here some example that will hopefully help
function notFound(req, res, next) {
res.status(404);
const error = new Error(`Not Found - ${req.originalUrl}`);
next(error);
}
function errorHandler(err, req, res, next) {
const statusCode = res.statusCode !== 200 ? res.statusCode : 500;
res.status(statusCode);
res.json({
message: err.message
});
}
app.use(notFound);
app.use(errorHandler);
Here is the problem that I am facing in express.
Somewhere in my express middleware, I want to check for the presence of a file.
//Setting up express middeleware...
app.use(f1);
app.use(f2);
...
function f1(req, res, next) {
...
//Here I want to check if 'somefile' exists...
fs.access('somefile', callback1, req, res, next);
}
//In the callback, I want to continue with the middleware...
function callback1(err, req, res, next) {
if (err) {
//Report error but continue to next middleware function - f2
return next();
}
//If no error, also continue to the next middleware function - f2
return next();
}
function f2(req, res, next) {
}
How do I pass req, res, next as arguments to the callback of fs.access?
The above code does not work. I suspect I need to use closures but how?
A totally different way of looking at the problem is: How do I use, for example, fs.access, itself as a express middleware function?
For me this approach have much more sense:
Assume you want to create a middleware in f1, and then have a middleware for error handling handleError, and any other middleware.
For f1 you already have the req, res in the closure so you will have access in fs.access callback.
function f1(req, res, next) {
fs.access('somefile', (err) => {
if (err) return next(err);
// here you already have access to req, res
return next();
}
}
function f2(req, res, next) {
// do some stuff if there is err next(err);
}
function handleError(err, req, res, next) {
if (err) {
// handle somehow the error or pass down to next(err);
}
}
app.use(f1); // you pass down the err |
app.use(f2); // ------------ |
app.use(handleError); // <----|
I am currently working on formBuilder (client javascript <=> JSON <=> node), so i need effective way to handle JSON data on server. All forms are bind on one route, catched by middleware, so i need something like this:
Code is simplified (no regexs, req validators etc ..)
var middleware = require('../middleware'); // simple dir to object export
exports = module.exports =function(req,res,next) {
if(req.xhr && req.is('application/json')) {
var i, items = req.body.events.length;
for(i = 0; i < items; i++) {
var event = req.body.events[i];
if(middleware.forms[event] {
// -----------------
and here add that middleware into current flow ..
// -----------------
}
}
} else {
return next();
}
Easiest way is to prepare list of middleware, which will be used and call them in final route witch async .. but that i donw regard this as good way ..
So, i there any way to add requested middlwares to current flow, but before filan route ?
Middleware are just functions. So there is nothing wrong with just calling them. I had the same problem last week and I wrote a little helper.
var walkSubstack = function (stack, req, res, next) {
if (typeof stack === 'function') {
stack = [stack];
}
var walkStack = function (i, err) {
if (err) {
return next(err);
}
if (i >= stack.length) {
return next();
}
stack[i](req, res, walkStack.bind(null, i + 1));
};
walkStack(0);
};
You can use it with an array or just one function.
walkSubstack(middleware, req, res, next);
//or
walkSubstack([middleware, middleware], req, res, next);
I wrote something very similar:
let isActive1 = false;
let isActive2 = false;
let func1MD = (req, res, next) { /* ... */ }
let func2MD = (req, res, next) { /* ... */ }
let middleware = (function () {
// middleware #1
function func1(req, res, next) {
if (!isActive1) { return next(); }
return func1MD.call(null, req, res, next);
}
// middleware #2
function func2(req, res, next) {
if (!isActive2) { return next(); }
return func2MD.call(null, req, res, next);
}
// Returning an array of all middlewares to be called sequentially
return [
func1,
func2
]
})();
app.use(middleware);