I was wondering how to get this done in nodejs.
I have a .get that looks like this
app.get('/someurl', function(req, res) {
response = somefunc_call_to_server();
res.send(response);
});
But this will be a blocking call, how to change this to handle parallel requests at /someurl?
thanks,
vik.
Node does not expose any APIs that allow you to synchronously request data from a server. The key thing that you seem to be assuming is that a response must be sent to the server synchronously in the callback, but that is not the case. The socket open to the browser will stay connected until it either times out, or the application triggers .end either directly, or via Express's helper methods such as .send.
Generally you would accomplish your task with something like this:
app.get('/someurl', function(req, res, next) {
somefunc_async_call_to_server(function(err, data){
if (err) return next(err);
res.send(data);
});
});
Related
I have a server that is fully functioning, but I only want it to be accessable when I say. I do this via a discord bot which works fine. I currently have a boolean variable server_on and an if (server on) { do } in all of my app.get and app.post functions. Is there a cleaner way to do this, or is this if statement in every function the only way?
Edit:
Final working code
var block_server_middleware = function (req, res, next) {
if (!server_on) { res.send("server is currently unavailable") }
else { next() }
}
app.use(block_server_middleware)
and the other app.get and app.post functions were not changed at all
This was the only few lines added that made the whole idea work
You can define one middleware function that goes before all your routes are defined:
app.use((req, res, next) => {
if (!server_on) {
// stop all routing
res.send("server not on!");
} else {
// let routing continue
next();
}
});
This will keep your routes from ever getting run until server_on is back to true. If you have any routes you want to leave open all the time, then just place them before this middleware.
You can replace res.send("server not on!"); with whatever is appropriate for your use. You can return a single web page or you can send back a 4xx or 5xx error status (perhaps a 503 error).
I just went through a tutorial and made a simple restful api. Afterwards, I added an admin user to my database. When I run the server, the authentication works, but if I try to access data via the api from the browser I get the eternal "waiting for response from localhost". I'm not sure what else I need to do for the request to be processed now that I've included a user authentication in mongodb.
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://admin:password#localhost/bookstore?authSource=admin");
app.get("/", function(req, res){
res.send("Hello World");
});
When going to localhost:3000/ I see hello world, but localhost:3000/api/books won't return anything
app.get("/api/books", function(req, res){
Genre.getBooks(function(err, books){
if(err){
throw err;
}
res.json(books);
});
});
Here's the getBooks function
module.exports.getBooks = function(callback, limit){
Book.find(callback).limit(limit);
};
You're not in any way handling the callbacks from Node correctly. You also haven't defined what Genre or Book is in your answer.
That said, a couple notes. First, if you are in a callback, you never want to throw an error (as in don't use the syntax throw err), instead you want to either handle it or pass it back to your calling code.
A common approach I use would be like this:
app.get("/api/books", function(req, res, next){ // note adding next here
Genre.getBooks(function(err, books){
if(err){
return next(err);
}
return res.json(books);
});
});
// sometime later, a generic error middleware. You can make yours more useful
app.use((err, req, res, next) => {
return res.status(500).send({ message: 'An error has occurred' });
});
Your mongoclient connection and getBooks syntax are also probably wrong, but I can't advise you the best way to fix it without knowing if you're using Mongoose or if you're doing something else.
Ok, so since you're using Mongoose, you should remove the MongoClient code you have. Instead, add at app startup:
const mongoose = require('mongoose');
mongoose.connect('your connectionstring here');
and your getBooks should look like this:
module.exports.getBooks = function(callback, limit){
Book.find({}).limit(limit).exec(callback);
};
As a sidenote, node apps typically use the callback as the final argument in a parameter list, so you might want to change the function signature to (limit, callback) and adjust the calling code accordingly.
I figured out the problem. I didn't have mongoose properly set as the client, so when referencing the exported code for finding the book data nothing happened.
Say I have this code in node.js:
app.post('/stuff', function(req, res){
res.send('hello world');
});
app.route('*').all(function(req,res,next){
res.write('Hello World');
});
There are 2 routes here - one is a catch-all for any request to the server and the other is what to do if a post request with a route of /stuff is sent.
Is it possible to pass a value local to the post request into route?
Intended Program flow: initial request to the server goes through route (which has controls not illustrated to ignore post requests) and the connection is left open, then a post request is sent from the initial request and is dealt with by app.post - can a value found within the closure of post be passed to the initial request that is still open?
I am trying to avoid using Global variables, and res.local only has a local scope. Also trying to keep clean so if I can use native express or node.js all the better. Much obliged for any help.
then a post request is sent from the initial request
Why don't you simply pull out the POST function and call it from both handlers, that way you don't need to send a seperate request from inside your app.
var postHandler = function(req, res) {
res.send('hello world');
// Just return whatever you want back to the calling function
return someValue;
};
// Set it as the handler for post.
app.post('/stuff', postHandler);
app.route('*').all(function(req,res,next){
// Call function.
var returnValue = postHandler(req, res);
res.write('Hello World');
});
app.route('*').all(function(req,res,next){
if (req.originalUrl === '/stuff') {
req.variable = 'some value';
return next();
};
res.write('Hello World');
});
app.post('/stuff', function(req, res){
var value = req.variable;
res.send('hello world');
});
You can try this code. Please take the order of app.all and app.post.
The second way, your can try app.locals http://www.expressjs.com.cn/4x/api.html#app.locals
The app.locals object is a JavaScript object, and its properties are
local variables within the application.
I have an Express.JS server with many res.json. To perform statistics, logging and debugging, I want to capture the response payload in a catch-all hook.
I have found the finish event res.on('finish') but I cannot find how to extract the response payload from it.
res.on('finish') is called once the data has been sent, so the payload doesn't necessarily exist on the server any more. You can add middleware to express to intercept every request and then override the .json method to log the data as the function is called:
router.use('/', (req, res, next) => {
var old = res.json.bind(res);
res.json = (body) => {
//Do whatever
old(body);
}
next();
})
Inspired by How to share sessions with Socket.IO 1.x and Express 4.x? i implemented socket authentication in some "clean" way where is no need to use cookie-parser and to read cookies from headers, but few items remain unclear to me. Example use last stable socket.io version 1.3.6.
var express = require('express'),
session = require('express-session'),
RedisStore = require('connect-redis')(session),
sessionStore = new RedisStore(),
io = require('socket.io').listen(server);
var sessionMiddleware = session({
store : sessionStore,
secret : "blabla",
cookie : { ... }
});
function socketAuthentication(socket, next) {
var sessionID = socket.request.sessionID;
sessionStore.get(sessionID, function(err, session) {
if(err) { return next(err); }
if(typeof session === "undefined") {
return next( new Error('Session cannot be found') );
}
console.log('Socket authenticated successfully');
next();
});
}
io.of('/abc').use(socketAuthentication).on('connection', function(socket) {
// setup events and stuff
});
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
app.use(sessionMiddleware);
app.get('/', function(req, res) { res.render('index'); });
server.listen(8080);
index.html
<body>
...
<script src="socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:8080/abc');
</script>
</body>
So io('http://localhost:8080/abc'); from client-side will send initial HTTP handshake request to server, from where server can gather cookies and many others request informations. So server has access to that initial request via socket.request.
My first question is why handshake request is not in scope of express-session middleware?(More generally in scope of app.use middlewares?) In some way i expected this app.use(sessionMiddleware); to fire before that initial request, and then to access easily to socket.request.session
Second, what are the scenarios in which middlewares defined with io.use() will fire? Only for initial HTTP handshake request? It seems like io.use() is used for socket related stuff(question is: what stuff), while app.use for standard requests.
I'm not quite sure why in the above example io.use() is fired before io.of('/abc').use(). Intentionally i wrote that order putting io.of('/abc').use() first to see will it work and it work.
Should have been written conversely.
Lastly, socket.request.res like pointed also from some people in linked question, sometimes is undefined causing app to broke, problem can be solved by providing empty object instead of socket.request.res, like: sessionMiddleware(socket.request, {}, next); which seems to me like a dirty hack. For what reasons socket.request.res yield to undefined?
Despite #oLeduc is kind of correct, there are a few more things to explain..
Why the handshake's request is not in scope of express-session middleware?
The biggest reason here is that the middleware in express is designed to handle request specific tasks. Not all, but most of the handlers use the standard req, res, next syntax. And sockets are "request-less" if I can say. The fact that you have socket.request is due to the way the handshake is made, and that it is using HTTP for that. So the guys at socket.io hacked that first request into your socket class so that you can use it. It was not designed by the express team to ever work with sockets and TCP.
What are the scenarios in which middlewares defined with io.use() will fire?
io.use is a close representation of the express use middleware way. In express, the middleware is executed on each request, right? But sockets do not have requests and it will be awkward to use middleware on each socket emit, so they've made it to be executed on each connection. But as well as the express middleware is stacked and used before the actual request is handled (and responded), Socket.IO uses the middleware on connection and even before the actual handshake! You can intercept the handshake if you want to, using that kind of middleware, which is very handy (in order to protect your server from spamming). More on this can be found in the code of passport-socketio
Why io.use() fires before io.of('/abc').use()?
The real explanation on this can be found here, which is this code:
Server.prototype.of = function(name, fn){
if (String(name)[0] !== '/') name = '/' + name;
if (!this.nsps[name]) {
debug('initializing namespace %s', name);
var nsp = new Namespace(this, name);
this.nsps[name] = nsp;
}
if (fn) this.nsps[name].on('connect', fn);
return this.nsps[name];
};
And in the beginning of the code, there is this line of code:
this.sockets = this.of('/');
So, there is a default namespace created at the very beginning. And right there, you can see that it has immediately a connect listener attached to it. Later on, each namespace gets the very same connect listener, but because Namespace is EventEmitter, the listeners are added one after another, so they fire one after another. In other words, the default namespace has it's listener at first place, so it fires first.
I don't think this is designed on purpose, but it just happened to be this way :)
Why is socket.request.res undefined?
To be honest, I'm not pretty sure about that. It's because of how engine.io is implemented - you can read a bit more here. It attaches to the regular server, and sends requests in order to make a handshake. I can only imagine that sometimes on errors the headers are separated from the response and that's why you won't get any. Anyways, still just guessing.
Hope information helps.
Why the handshake's request is not in scope of express-session middleware?
Because socket.io will attach to a http.Server which is the layer under express. It is mentioned in a comment in the source of socket.io.
The reason for this is because the first request is a regular http request used to upgrade the reqular stateless http connection into a state-full websocket connection. So it wouldn't make much sense for it to have to go through all the logic that applies to regular http requests.
What are the scenarios in which middlewares defined with io.use() will fire?
Whenever a new socket connection is created.
So every time a client connects it will call the middlewares registed using io.use(). Once the client is connected however, it is not called when a packet is received from the client. It doesn't matter if the connection is initiated on a custom namespace or on the main namespace, it will always be called.
Why io.use() fires before io.of('/abc').use()?
Namespaces are a detail of socket.io's implementation, in reality, websockets will always hit the main namespace first.
To illustrate the situation, look at this snippet and the output it produces:
var customeNamespace = io.of('/abc');
customeNamespace.use(function(socket, next){
console.log('Use -> /abc');
return next();
});
io.of('/abc').on('connection', function (socket) {
console.log('Connected to namespace!')
});
io.use(function(socket, next){
console.log('Use -> /');
return next();
});
io.on('connection', function (socket) {
console.log('Connected to namespace!')
});
Output:
Use -> /
Main namespace
Use -> /abc
Connected to namespace!
See the warning that the socket.io team added to their documentation:
Important note: The namespace is an implementation detail of the Socket.IO protocol, and is not related to the actual URL of the underlying transport, which defaults to /socket.io/….
Why is socket.request.res undefined?
As far as I know it should never be undefined. It might be related to your specific implementation.