exit from chain of route specific middleware in express/ nodejs - javascript

I have a chain of "route specific middleware" for this route, like so:
var express = require('express');
var server = express();
var mw1 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
req.connection.destroy(); //without calling next()
}
};
var mw2 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
req.connection.destroy(); //without calling next()
}
};
server.post('/some/path', [mw1, mw2], function(req, resp) {
//write response
});
[mw1, mw2] are the middleware specific to the route /some/path.
This is different from server-wide middleware like this:
server.use(mw1);
server.use(mw2);
Where it applies to all routes defined.
Now my issue is that I want to exit from the chain. I.e. if success is false in mw1, I do not wish for mw2 to be called. If success is false in mw2, I do not without for the route function to be called. Presently, both mw1 and mw2 appear to be getting called whether or not next() is called - and I do not know why.
How can I go about doing this?

You can call next( 'route' ), as said on the express api reference, application routing section:
Multiple callbacks may be given, all are treated equally, and behave
just like middleware, with the one exception that these callbacks may
invoke next('route') to bypass the remaining route callback(s).
Example
var express = require('express')
, app = express()
;
// keep an eye on the function names
app.post( '/some/path', middleware1, middleware2, function route1( req, res, next ) {
// write response
});
app.all( '*', function route2( req, res, next ) {
// do something like handle a 404 request
});
app.use(function errorHandler( err, req, res, next ) {
// handle error
});
function middleware1( req, res, next ) {
// ...
if ( !success ) {
// bypasses middleware2 and route1, route2 will be called
return next( 'route' );
}
// calls middleware2
next();
}
// intentionally similar to middleware1 to keep things clear
function middleware2( req, res, next ) {
if ( !success ) {
// bypasses route1 and route2
// errorHandler will be called with the error
return next( Error( 'middleware 2 failed' ) );
}
// calls route1
next();
}

A little more tinkering yielded the answer:
var express = require('express');
var server = express();
var mw1 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
resp.send(406, 'Invalid because of this');
req.connection.destroy(); //without calling next()
}
};
var mw2 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
resp.send(406, 'Invalid because of that');
req.connection.destroy(); //without calling next()
}
};
server.post('/some/path', [mw1, mw2], function(req, resp) {
//write response
});
The trick was send a response: resp.send(406, 'Invalid because of this');
Just prior to destroying the connection: req.connection.destroy();
In fact not destroying the connection, I found to also work, in the general case.
(But was required in my specific case, and is out of the scope of this question.)
If the response has already been sent, then express does not automatically call next() for you, as it appeared to do otherwise.

I was under the impression that if you neither call next() nor send a response in a route handling function, express just hangs. Also FWIW I haven't used an array, mine looks like server.post('/some/path', mw1, mw2, function(req, resp) {...
Anyway. One alternative might be to restructure your code so you only have a single handling function. Do you have a good reason for mw1 and mw2 being middleware instead of regular async functions your handler calls?
var express = require('express');
var server = express();
var mw1 = function(req, res, callback) {
// do stuff with req/res if necessary but don't send a response
if (success) {
callback(null);
} else {
callback('Error');
}
};
var mw2 = function(req, res, callback) {
//do other stuff but don't send a response
if (success) {
callback(null);
} else {
callback('Error');
}
};
function mwBoth(req, res){
mw1(req, res, function(err){
if(err){ return res.send(500) };
mw2(req, res, function(err){
if(err){ return res.send(500) };
// neither had an error
res.redirect('/some/other/path');
});
});
};
server.post('/some/path', mwBoth);

Related

Pass Parameter to Function Express JS

everything good ? I would like to know the best way and if it is possible to send a certain parameter to an express function in the NodeJS.
i wanna pass the string ('admin') or other string that i want to function 'RoleTolen', using my route.
function verifyToken
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
if (typeof bearerHeader !== "undefined") {
// Split at the space
const bearer = bearerHeader.split(" ");
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
function ValidToken
function validToken(req, res, next) {
jwt.verify(req.token, process.env.SECRET, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
next();
}
});
}
function RolesToken
function rolesToken(role, req, res, next) {
var decoded = jwt.decode(req.token);
if (!decoded.permissions.includes(role)) {
res.json("sem permissao");
} else {
next();
}
}
my route
router.post(
"/posts",
verifyToken,
validToken,
rolesToken("admin"),
customerController.posts
);
function rolesToken(role, req, res, next) is not a proper Express.js route handler, as you already know (and hence the question, I assume).
What you can do is to write your rolesToken as a function that returns the actual Express.js compliant handler (function):
function rolesToken(role) {
return function(req, res, next) {
var decoded = jwt.decode(req.token);
if (!decoded.permissions.includes(role)) {
res.json("sem permissao");
} else {
next();
}
};
}
from the docs
Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature (err, req, res, next)):
Therefore your RolesTaken function won't work as expected.
Secondly, you can create a middleware or modify existing one
(req, res, next) => {
//some stuff
if(req.path == '/posts') req.role= 'admin';
if(req.path == '/someOtherPath') req.role= 'someRole';
//some other stuff
}
Now you can get the get the role in req.someProp in the your controller middlerware as req.role
you would like to see this question also

res.redirect is not a function in express

I'm currently trying to redirect to an external site with node and express specifically in a get call. However, I can't seem to find a possible solution. Any help would be appreciated. Note that when trying response.redirect I'm getting TypeError: res.redirect is not a function. However, when I view the express documentation it seems to be in there.
app.get('/:urlToForward', (res, req, next)=>{
//Stores the value of param
// var shorterUrl = res.params.urlToForward;
// shortUrl.findOne({'shorterUrl': shorterUrl}, (err,data)=>{
// // if (err) {
// // res.send("This shorterUurl does not exist.");
// // }
// // else {
// // res.redirect(301, data.originalUrl);
// // }
// // response.end();
// });
res.redirect('https://www.google.com');
});
Order matters in the arguments. req must be first, then res, then next.
app.get('/:urlToForward', (req, res, next)=>{ ...
You can do res.redirect('http://app.example.io');
Express docs: http://expressjs.com/api.html#res.redirect
Just use simple:
app is instance of invoked Express application.
app.get('/', function(request,respond) {
respond.redirect('your_url'); //Pass between the brackets your URL.
});
Note you can use ES6 shorthand for shorterUrl, no need to type it out twice.
app.get('/:urlToForward', (req, res, next)=> {
//Stores the value of param
var shorterUrl = res.params.urlToForward;
shortUrl.findOne({shorterUrl}, (err, data)=> {
if (err) {
res.send("This shorterUrl does not exist.");
}
else {
res.redirect(data.originalUrl);
}
response.end();
})
});

Use an array of middlewares at express.js

I'm trying to use an array of middlewares. Well, more like a combination of function names and arrays.
Instead of having:
router.post('/editPassword', validate, changePassword, sendConfirmation);
I would like to have something like:
router.post('/editPassword', validate, [changePassword, sendConfirmation] );
That would look like:
router.post('/editPassword', validate, doAction );
Where doAction would be an array like this:
var doAction = [
//equivalent of changePassword
function(req, res, next){
//whatever
next();
},
//equivalent to the previous sendConfirmation
function(req, res, next){
//whatever
}
]
But it seems it is failing and going back to the validate step after the next() within the first function in doAction.
I'm looking for a way to simplify the middleware chaining including some middleware steps under the same name.
Latest version of Express can handle this:
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod (req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('User Info')
})
You can review more from this link
I assume the reason you wanted it to look that way is not only for it to appear presentable, but also to be able to reuse the other middleware. In that case, you can create a middleware which runs all other middlewares to do the check for you, and only calls the next function if all validations succeed.
var express = require('express');
var app = express();
function middleware1(req, res, next) {
if(req.query.num >= 1) {
next();
} else {
res.json({message: "failed validation 1"});
}
}
function middleware2(req, res, next) {
if(req.query.num >= 2) {
next();
} else {
res.json({message: "failed validation 2"});
}
}
function middleware3(req, res, next) {
if(req.query.num >= 3) {
next();
} else {
res.json({message: "failed validation 3"});
}
}
function combination(req, res, next) {
middleware1(req, res, function () {
middleware2(req, res, function () {
middleware3(req, res, function () {
next();
})
})
})
}
app.get('/', combination, function (req, res) {
res.send('Passed All Validation!');
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
You can test this app by running it then viewing http://localhost:3000/?num=3, changing the value 3 to a lower number, or removing the num parameter.
I'm not sure if this is the proper way to do it, but this is how I've handled my other projects. Let me know what you think.
note: see comments for use case. #robertklep may have a better solution depending on how you want to use middlewares
Just search a little more ^^ : Less ugly and more understandable than previous answer
https://github.com/blakeembrey/compose-middleware
Be careful that you're not doing (the equivalent of) this in your validate middleware:
function middleware(req, res, next) {
if (someCondition) {
console.log('some condition is true');
next();
}
console.log('some condition is false');
res.status(400).end();
}
The intention here is that after calling next the rest of the code isn't executed, but it will. There's nothing really special about next, so when you call it, after it returns the middleware code continues to run (causing both "some condition is true" and "some condition is false" to be logged).
That's why you often see this:
if (someCondition) {
console.log('some condition is true');
return next();
// Or, alternatively:
// next();
// return;
}
The return causes the middleware function to return after calling next, so the rest of the code in the function won't be executed.
This functionality is already built into express as an array or middleware:
let combined = express.Router()
.use(
[
middleware1,
middleware2,
middleware3,
],
);
let combined = express.Router()
.use(
middleware1,
middleware2,
middleware3,
);
Full Example
"use strict";
let Http = require("http");
let Express = require("express");
let server = Express();
let app = Express.Router();
let combined = Express.Router();
combined.use(
function (req, res, next) {
console.log("huzzah!");
next();
},
function (req, res, next) {
res.json({ success: true });
}
);
function middleware0(req, res, next) {
console.log('ground zero');
next();
}
app.get("/combined", middleware0, combined);
server.use("/", app);
Http.createServer(server).listen(3000);

node express: mock authorize function in app.get

I'm practicing with simple .get methods in node/express. I'm following an example of a book, but I have no session variable and i have no templates; so i've commented that lines and i've replaced them with a simple .send method, and i've replaced with a simple hardcode variable: authorized.
I'm getting the error: ReferenceError: res is not defined
The problem is that I have no res variable, because the control pass first on authorize function.
function authorize(req, res, next){
authorized = true;
// if(req.session.authorized) return next();
if(authorized) return next();
res.send('not-authorized');
}
app.get('/secret', authorize, function(){
// res.render('secret');
res.send('secret');
});
app.get('/sub-rosa', authorize, function(){
// res.render('sub-rosa');
res.send('sub-rosa');
});
thanks to comments, the solution is:
function authorize(req, res, next){
authorized = true;
// if(req.session.authorized) return next();
if(authorized) return next();
res.send('not-authorized');
}
app.get('/secret', authorize, function( req, res ){
// res.render('secret');
res.send('secret');
});
app.get('/sub-rosa', authorize, function( req, res ){
// res.render('sub-rosa');
res.send('sub-rosa');
});

node.js/express chaining multiple get commands

I have a route I that in order to get all the data needs to access the API server multiple times (according to the data that was given).
Now I need to add a third access to the server and it's getting rather unreadable.
The following code is working, but I have a feeling I'm not doing it right (promises?) - couldn't figure out what exactly is recommended in this case
The code: (stripped down to emphasise the point)
router.get('/', function(req, main_response) {
http.get(FIRST_API_COMMAND, function (res) {
var moment_respose_content = '';
res.on("data", function (chunk) {
moment_respose_content += chunk;
});
res.on('end',function(){
if (res.statusCode < 200 || res.statusCode > 299) {
main_response.send('error in getting moment');
return;
}
var response = JSON.parse(moment_respose_content );
if (response.success)
{
var data = response.data;
//doing something with the data
http.get(SECOND_API_COMMAND, function (res) {
res.on("data", function (chunk) {
comment_respose_content += chunk;
});
res.on('end',function(){
var response = JSON.parse(comment_respose_content);
if (response.success)
{
var comments = response.data;
main_response.render('the page', {data: data});
return;
}
});
}).on('error', function (e) {
console.log("Got error: " + e.message);
main_response.send('Error in getting comments');
});
return;
}
});
}).on('error', function (e) {
console.log("Got error: " + e.message);
main_response.send('Error in getting moment');
});
});
You can write a middleware for each remote action, and then use those middlewares before the get handler, so the get handler can simply access their results. (Promises can help if you need to start subsequent requests before waiting for earlier ones to finish, but that situation is rare.)
For example, using express middleware to fetch each remote data independently:
var request = require('request');
var express = require('express');
var app = express();
var router = express.Router();
/* middleware to fetch moment. will only run for requests that `router` handles. */
router.use(function(req, res, next){
var api_url = 'https://google.com/';
request.get(api_url, function(err, response, body) {
if (err) {
return next(err);
}
req.moment_response = response.headers["date"];
next();
});
});
/* middleware to fetch comment after moment has been fetched */
router.use(function(req, res, next){
var api_url = 'https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new';
request.get(api_url, function(err, response, body){
if (err) {
return next(err);
}
req.comment_response = parseInt(body);
next();
});
});
/* main get handler: expects data to already be loaded */
router.get('/', function(req, res){
res.json({
moment: req.moment_response,
comment: req.comment_response
});
});
/* error handler: will run if any middleware called next() with an argument */
router.use(function(error, req, res, next){
res.status(500);
res.send("Error: " + error.toString());
});
app.use('/endpoint', router);
app.listen(8000);
Often the remote data you want to fetch is based on some parameter of the main request. In this case you would want to use req.param() instead of App.use() to define the data-loading middleware.

Categories