I wrote a middleware for Connect and Express that requires some heavy lifting in its setup method. Due to the nature of the initialization tasks this stuff is asynchronous, so I have the problem that the middleware shall only be accessible once the initialization has been run.
Currently I have solved it using a callback:
function setupMiddleware(callback) {
doSomeAsyncInitialization(function () {
callback(function (req, res, next) {
// The actual middleware goes here ...
});
});
}
This works, but it's not nice for the caller. Instead of being able to do
app.use(setupMiddleware());
I have to do:
setupMiddleware(functin (middleware) {
app.use(middleware);
});
Now I was thinking whether there is a better approach, e.g. let the middleware initialize in the background and delay all incoming requests until the middleware is ready.
How could I solve this? Any ideas or best practices that I should use here?
I now solved it using an isInitialized variable and delaying the middleware function itself. See the following example:
var connect = require('connect');
var setup = function () {
var isInitialized = false;
setTimeout(function () {
isInitialized = true;
}, 10000);
function run (req, res, next) {
res.write('Foo');
res.end();
}
function delay (req, res, next) {
if (isInitialized) {
return run(req, res, next);
}
setTimeout(function () {
delay(req, res, next);
}, 1000);
}
return function (req, res, next) {
if (req.url === '/foo') {
delay(req, res, next);
return;
}
next();
}
};
var app = connect();
app.use(setup());
app.use(function (req, res) {
res.write('Fertig!');
res.end();
});
var http = require('http');
http.createServer(app).listen(5000);
Please note that this code has not been optimized or refactored in any way, it just is a demonstration that the idea itself works.
Why you don't do like as follows,
doSomeAsyncInitialization(function () {
//After doing all long running init process just configure your express as follows.
app.use(<middlewares>);
app.listen(<portnumder>);
});
Related
How to implement middleware like this in socket.io? Please help
EXPRESS APP
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
app.get('/', function (req, res) {
res.send('Hello World!')
})
SOCKET APP (I am using express pattern but its not working)
var myLogger = function (data,next) {
console.log('DOING DATA VALIDATION...')
next()
}
io.use(myLogger)
io.on('someEvent/', function (data, callback) {
callback('Hello World!')
})
Error : next() is not define!
app.use(function (req, res, next) {
req.io = io;
next();
});
This assigns a socket object to every request.
If somebody's still wondering.
To use middleware on all sockets:
io.use((socket, next) => {
// isValid is just a dummy function
if (isValid(socket.request)) {
next();
} else {
next(new Error("invalid"));
}
});
This example is from the official docs of socket.io
To use a middleware for a specific client:
io.on('connection', async (client) => {
client.use((socket, next) => {
console.log(`got event: ${socket[0]} in client middleware, moving on with next() just like in express`)
next()
});
// rest of your code
newConnection(client)
})
I am trying to make one router for login page post and get request.
below is my code :
app.get('/login', getLoginPage);
app.post('/login', doLogin);
Here I am using app.all to combine both , but I am not getting any idea how to handle it in all.
What I have tried so far .
app.all('/login', handler);
var handler = function(req, res){
console.log(req);
if(req.method=="POST")
{
getLoginPage;
}
if(req.method=="GET")
{
doLogin
}
}
const {getLoginPage,doLogin} = require('./controller/login');
Here app.get and app.post working fine for me except app.all.
Here I am using express js.
Please suggest .
I would do it like so (full working example):
var express = require('express');
var app = express();
app.route('/test')
.get((req, res) => getLoginPage(req, res))
.post((req, res) => doLogin(req, res))
doLogin = (req, res) => {
res.send('doLogin');
}
getLoginPage = (req, res) => {
res.send('getLoginPage');
}
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
HOISTING!!!
It's really just scope. You have a function expression, not a declaration. Declarations get hoisted but definitions do NOT. So when you call handler, handler is not defined yet since it's function expression comes bellow it.
So no need to change your code, except the order of things. Do this and it will work
var handler = function(req, res) {
if (req.method == "POST") {
console.log("POST");
}
if (req.method == "GET") {
console.log("GET");
}
};
app.all("/login", handler);
Read about scopes in function declaration vs expression
You need to invoke the functions in your app.all handler.
app.all('/login', handler);
var handler = function (req, res) {
console.log(req);
if (req.method == "POST") {
getLoginPage(req, res);
}
if (req.method == "GET") {
doLogin(req, res);
}
}
Try following use method. It will accept all types of methods and you can apply conditions there:
app.use('/login', function (req, res, next) {
console.log('Request Type:', req.method)
if(req.method=="POST")
{
getLoginPage;
}
else if(req.method=="GET")
{
doLogin;
}
});
Tried invoking the functions and pass the req and res parameters to those functions
app.all('/login', handler);
function handler(req, res){
if(req.method=="POST"){
getLoginPage(req, res);
} else if(req.method=="GET"){
doLogin(req, res);
}
}
edit: change to function declaration instead of function expression to avoid hoisting issues
I am new to node and express. I have seen app.get and app.post examples using both "res.send" and "return res.send". Are these the same?
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.type('text/plain');
res.send('i am a beautiful butterfly');
});
or
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.type('text/plain');
return res.send('i am a beautiful butterfly');
});
The return keyword returns from your function, thus ending its execution. This means that any lines of code after it will not be executed.
In some circumstances, you may want to use res.send and then do other stuff.
app.get('/', function(req, res) {
res.send('i am a beautiful butterfly');
console.log("this gets executed");
});
app.get('/', function(req, res) {
return res.send('i am a beautiful butterfly');
console.log("this does NOT get executed");
});
I would like to point out where it exactly made a difference in my code.
I have a middleware which authenticates a token. The code is as follows:
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1] || null;
if(token === null) return res.sendStatus(401); // MARKED 1
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if(err) return res.sendStatus(403); // MARKED 2
req.user = user;
next();
});
}
On the // MARKED 1 line, if I did not write return, the middleware would proceed and call next() and send out a response with status of 200 instead which was not the intended behaviour.
The same goes for like // MARKED 2
If you do not use return inside those if blocks, make sure you are using the else block where next() gets called.
Hope this helps in understanding the concept and avoiding bugs right from the beginning.
app.get('/', function(req, res) {
res.type('text/plain');
if (someTruthyConditinal) {
return res.send(':)');
}
// The execution will never get here
console.log('Some error might be happening :(');
});
app.get('/', function(req, res) {
res.type('text/plain');
if (someTruthyConditinal) {
res.send(':)');
}
// The execution will get here
console.log('Some error might be happening :(');
});
To Add a little bit more context to the examples above. Express has layers. So if you return in your function you end the execution. If you not end that you can go further in your layered logic.
So the next function passed to each layer can be called to execute the next layer. If you don't call next the execution stops after your method excuted is executed. (Return just exits the function)
The response object is still available after sending. It is just not possible to write to it again because it has already completed after you did res.end() or res.send().
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
// a controller handles a http request and terminat it
const controller = (req, res, next) => {
// return http response to client
res.send('hello world');
// do something after you sended request
console.log('do something else');
// if you call next the request will go to the next layer -> afterSend,
// if you do not call next the execution will end
next();
};
// this middleware/layer is executed after response is send to client
const afterSend = (req, res, next) => {
// do something after you sended request, but not send again -> readonly
console.log(res);
// this would throw an error
// res.send()
// res.end()
// etc...
};
// we skip routers here
app.get('/hello', controller, afterSend);
app.listen(port, () => {
console.log(`Running on ports ${port}`);
});
Here's my code
mysql = require('mysql');
con = mysql.createConnection(connectionSetttings);
app.get('/users/list', ensureLogin, function (req, res, next) {
con.query(query.getUserList, [req.user.innId], function (err, rows) {
if (err) {
next(err); // also tried: return next(err)
} else {
displayUsers();
}
});
}, function (err) {
console.error(err);
displayErrorPage(500);
});
I run it with the MySQL server not running to see if Express would render the error page or not. The problem is, Express simply barfs up the stack into the browser. Is there anything wrong with my code? How do I properly chain middlewares?
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next).
Make sure to add the error handling to the app afterthe routes.
var app = require('express')();
app.get('/users/list', ensureLogin, function (req, res, next) {
con.query(query.getUserList, [req.user.innId], function (err, rows) {
if (err) {
next(err);
} else {
displayUsers();
}
});
});
// Error handling middleware
app.use(function (err, req, res, next) {
displayErrorPage(500);
});
When using MySQL as a session store, session-related middlewares attached using app.use() will intercept requests for all routes and malfunctions. This will make seemingly unrelated routes throw HTTP500 even if it doesn't look like they invoke return next(err).
The solution to this is to only attach any session-related middleware to necessary routes. Alternatively, you can wrap them in a filter function like this one:
var middlewareFilter = function(middleware) {
// Return whatever function that is returned by this function
// to be used in app.use()
return function (req, res, next) {
// Set the routes to be ignored.
var exceptionList = [
'images',
'stylesheets',
'scripts'
];
// Cast req.url to string so it can use JS's string methods
var path = String(req.url);
// If path matches any of the exceptionList...
if (array.contains(path.split('/')[1], exceptionList)) {
// ...just do nothing
return next();
} else {
// Otherwise, return the middleware
return middleware(req, res, next);
}
}
}
And use them like app.use(session(sessionConfig));
I implemented a very simple middleware to check the permissions for the user:
app.js
...
var security = require('./lib/security');
app.use(security.init);
...
lib/security.js
var session;
var request;
var response;
function init(req, res, next) {
request = req;
response = res;
session = req.session;
next();
}
function adminRequired(){
if (!isAdmin()){
response.redirect('/login');
response.end();
return true;
}
return false;
}
...
The best way I found to interrupt the flow is the following:
routes/mycontroller.js
router.get('/', function(req, res, next) {
if(security.adminRequiredHtml()){return;} // now it actually interrupt the execution
res.render('admin',{});
res.end();
});
However, I would like to use it like this:
routes/mycontroller.js
router.get('/', function(req, res, next) {
security.adminRequiredHtml(); // <- interrupt the request
res.render('admin',{});
res.end();
});
It correctly perform the redirect, but the execution continues :(
I've tried a few solutions like but it doesn't really work:
response.end() -> close the output but continues the execution
process.end() -> it's too radical, terminates the execution but it also kill the server :(
I've been thinking about using a throw but I don't know where to catch it and make it terminate gracefully (no stacktrace)
You could create a custom Router that is secured and add your secure Routes to that:
var secureRouter = express.Router();
// every request on this router goes throug this
secureRouter.use('*', function (req, res, next) {
if(isAdmin()) next();
// if you don't call next() you interrupt the request automaticly
res.end();
});
// protected routes
secureRouter.get('/user', function(req, res){/* whatever */});
secureRouter.post('/user', function(req, res){/* whatever */});
app.use(secureRouter);
// not protected
app.get('/api', function(req, res){/* whatever */});
Express doc for using middlewares
You're actually looking for middleware, I think.
function myMiddleware (req, req, next) {
if (!isAdmin()) {
res.redirect('/login');
res.end();
} else {
//Proceed!
next()
}
}
router.get('/', myMiddleware, function(req, res, next) {
res.render('admin',{});
res.end();
});
You can chain as many of those as you'd like to handle whatever logic you need. Just make sure you call next() if you're supposed to move on!