Send the request to next event handler in NodeJS - node.js

I am trying to create a module which can log some certain params for the request and print them to the page which can be checked online, the page will use the socket.io to load the latest logs.
And I want this module can worked as a plugin which means you just call this module, and initialize it, then an extra entry point /_logger will be added to you application, once you visit the page, the latest logs will be updated in real-time. So the module have to intercept the requests:
function setup(httpServer) {
//page
httpServer.on("request", function (request, response) {
var pathname = url.parse(request.url).pathname;
if (pathname === '/_logger') {
fs.readFile(__dirname + '/logger.html', (err, data) => {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write(data);
response.end();
});
}else{
// how to give up the control for this requset
}
});
var io = require('socket.io')(httpServer);
io.on('connection', function (socket) {
//TO BE DONE
socket.on('event', function (data) { });
socket.on('disconnect', function () { });
});
}
module.exports = {
setup: setup
}
Usage:
var logger= require("./logger/index");
var server = require('http').createServer();
logger.setup(server);
server.on("request", function(req,res){
//Normal logic for different application
});
server.listen(3333);
Now the problem is that once the requested url is not /_logger, I should release the control of this request.
if (pathname === '/_logger') {
//take control
}else{
// Nothing should be done here, it should go to the next request chain.
}
After read the documents, I can not find the right way to make it.
Any ideas?

Assuming that you want to use low-level NodeJS HTTP API. You can compose several handlers into one handler using function composition. Each handler should yield the execution to the next handler, if the req.url doesn't matches.
var http = require('http');
var handler1 = function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('/');
res.end();
}
var handler2 = function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('/Hello');
res.end();
}
var middleware = compose([wrapHandler('/', handler1),
wrapHandler('/hello', handler2)]);
http.createServer(middleware).listen(3000);
function wrapHandler(path, cb) {
return function (req, res, next) {
if (req.url === path) {
cb(req, res);
} else {
next();
}
};
}
function notFoundHandler(req, res) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.write('No Path found');
res.end();
};
// adapted from koa-compose
function compose(middleware) {
return function (req, res){
let next = function () {
notFoundHandler.call(this, req, res);
};
let i = middleware.length;
while (i--) {
let thisMiddleware = middleware[i];
let nextMiddleware = next;
next = function () {
thisMiddleware.call(this, req, res, nextMiddleware);
}
}
return next();
}
}
In your case, you can write.
var loggerHandler = wrapHandler('/_logger', logger.handler);
httpServer.on('request', compose(loggerHandler, handler2, handler3));

httpServer.on("request", ...) is just one request listener. It is under no obligation to process the request if it doesn't need to. Even if it does nothing, any other request listeners will still get notified of this request.
If there are other request listeners (which you are implying that there are), then you can just do nothing in the request listener you show and the other listeners will also get a shot at the particular request. This allows you to add your own request listener to a working http server and your listener only has to pay attention to the new route that it wants to support and can just ignore all the other routes and they will get handled by the other listeners that are already in place.
Now, there are frameworks built to make this both simpler and to give you more control. In general, these frameworks use one listener and they provide a means for you to handle the request OR explicitly tell the framework that you have not handled the request and would like other route handlers to have a shot at handling the request. This is a bit more flexible than just have multiple listeners, all of which will get notified of the same route.
For example, using the Express framework, you can do this:
var express = require('express');
var app = express();
// route handler for / request only when a user=xxx is in the query string
app.get('/', function(req, res, next) {
// if user was included in query parameter
if (req.query.user) {
// do something specific when ?user=xxxx is included in the URL
} else {
// pass handling to the next request handler in the chain
next();
}
});
// route handler for / request that wasn't already handled
app.get('/', function(req, res) {
// handle the / route here
});
app.listen(80);

Related

node js - web page sometimes raise 404 error

my web site sometimes raise 404 error even though there is correct path in my index file.
When occurs 404 error, I reload the page and the page correctly uploaded.
even though by reloading the page successfully page uploaded, I think it's not stable...
Do you have any idea? I don't know why it happens.
This is my route and handler to main page!
And I used vhost to connect domain
app1.get('/',(req,res)=>{
var arr = poplist;
var type = 'recommended';
var session = null;
if(req.session.user){
session = req.session;
}
if (req.session.series_id){ /*5*/
var search_car = req.session.series_id;
arr.unshift({'id':req.session.series_id,'series_name':req.session.manu_name + ' ' + req.session.series_name});
type = 'mycar';
} else {
var search_car = recommend_car(arr).id; /*4*/
}
get_recommend_container(arr,function(err,recommend_container){
if (err) { res.send('err') }
else {
get_best_reviews_container(function(err,best_reviews_container){
if (err) { res.send('err') }
else {
/*search_car for query in menubar*/
res.render('main', {'type':type,'poplist':poplist,'recommend_container':recommend_container,'best_reviews_container':best_reviews_container,'search_car':search_car,'session':session});
}
});
}
});
});
....
app.use(vhost('mysite.com', app1));
app.use(function(req, res, next) {
res.status(404).send('Sorry cannot find the page!');
});
app.listen(8000,'myip',function(){
console.log("Connected to 8000 port!..")
});
You explicitely define a "404 middleware" :
app.use(function(req, res, next) {
res.status(404).send('Sorry cannot find the page!');
});
This means that every time your app will receive any request, it will execute that middleware that will send back a 404.
Documentation : https://expressjs.com/en/guide/using-middleware.html
This example shows a middleware function with no mount path. The
function is executed every time the app receives a request.
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})

Getting body content from request in node-mitm

I have the following node-mitm code.
mitm = Mitm();
mitm.on("request", function(req, res) {
const body = req.body; //body is null
})
I feel this has to do with reading node's IncomingMessage events, but I don't know how to do it.
Mitm.js's request handler is just like the one you're used to on Node's side. That is, it doesn't do anything special with req.body and leaves it as a ReadableStream.
You could either get its contents with the classical on("data") pattern:
mitm.on("request", function(req, res) {
req.on("data", function(data) { data == "Hello" })
})
If you want to fake a larger service, I've sometimes used Express to create routes and then pass Express's route handler to Mitm:
var Router = require("express").Router
var router = Router().use(require("body-parser").text())
router.get("/", function(req, res) { req.end() })
mitm.on("request", route.bind(null, router))
function route(router, req, res) {
router(req, res, function(err) {
if (err == null) return
res.writeHead(502)
throw err
})
}
The last example is a summary of the pattern I've also got publicly visible at the Rahvaalgatus open source repository: https://github.com/rahvaalgatus/rahvaalgatus.
Specifically, look at the controller test of https://github.com/rahvaalgatus/rahvaalgatus/blob/6dc91b026d75879cdc552bd2e63f220235b786c0/test/controllers/home_controller_test.js and see the this.router definition at https://github.com/rahvaalgatus/rahvaalgatus/blob/6dc91b026d75879cdc552bd2e63f220235b786c0/test/mitm.js.

request module is causes express.js to set headers

I'm using node with express and request modules. I could call res.json before I started using request to fetch info from another server. However, as soon as I'm trying to use res.json in the callback function in request, I would get the error message that the header has already been sent.
One solution would be to set the format header explicitly as 'application/json', however I don't want to unwrap res.json. Are there any other solutions to this? Has this been caused by the fact that express believes no header has been set and so presumptuously sends one on its own?
Code sample:
`
router.get('/app/:action', function (req, res) {
switch(req.params.action) {
case "search_gifs":
//res.json(["no problem"]);
request(
{/*some params*/},
function (error, response, body) {
res.json(["error"]);return;
}
);
break;//I didn't add break but nothing is happening in default, but I'll try again with break
}
`
As you said, you are getting error like this header has already been sent.
Let me explain you in simple way,
You must be written res.json from multiple places as per your condition.
You are getting this error because res.json is executing multiple times.
When it execute first time it will not give you error but second time it will give you error because response is already sent.
Your code has some loop hole. Debug it.
Let me try to explain you in details with example here.
const express = require('express');
const app = express();
//
// To generate error
//
app.get('/generate_error', function(req, res) {
//
// When check_error query param is true, It will throw you error else
// all good
//
if (req.query.check_error) {
//
// check_error is true so let's send response first here.
//
res.send('Hello World!');
}
//
// If check_error is true then it will try to send response again for
// same request.
// If check_error is false then it will by send response first time
// here so no error
//
res.send('Hello World!');
});
//
// Solution 1 for above case
//
app.get('/ignore_error_1', function(req, res) {
if (req.query.check_error) {
res.send('Hello World!');
} else {
res.send('Hello World!');
}
});
//
// Solution 2 but different with solution 1
//
app.get('/ignore_error_2', function(req, res) {
if (req.query.check_error) {
//
// When you don't have anything to execute after sending response
// then just add return here.
// In other case you have to manage your code as per your
// requirement if you have anything needs to be executed after
// sending response from multiple places.
// NOTE : Make sure you will not execute res.json multiple times
//
return res.send('Hello World!');
}
return res.send('Hello World!');
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!')
});
Just execute this three get urls
/generate_error?check_error=true
/ignore_error_1?check_error=true
/ignore_error_2?check_error=true

How to have express server make an initial http request before serving static files

I have a basic express server being used for an api server and serving static files for a react app like so:
app.use(express.static('/public'))
app.use('/api', apiRouter)
When a user initially loads the app, I need to retrieve data from a different server that I'll need for the api server before serving up the static files. Is there a way to have express make an initial http request to another server and cache the results on the server before serving up the static files?
Ideally, I want the server to make this request only once (when the user initially loads the app). I've looked into using express middleware, but am having trouble figuring out where to insert the middleware so that it only gets called when the files are served up.
First off, if you really want to only make a request once to some outside server, then you should probably just do it upon server startup. There's really no reason to wait until some request comes in if you're going to cache the result anyway and it doesn't matter what route or what user is making the first request.
But, if you want to trigger it only when a request comes in, then you can use middleware to do so. An outline of the middleware could look something like this:
let cachedResult;
app.use((req, res, next) => {
if (cachedResult) {
next();
} else {
request(..., (err, response, body) => {
if (err) {
next(err);
} else {
cachedResult = ....
next();
}
});
}
});
If you want the middleware to ONLY execute when a static resource is requested, then you need some way of determining which incoming requests are for static resources and which are not.
The code you show:
app.use(express.static('/public'))
checks the public sub-directory for a match for any request which isn't very helpful. If you would prefix all static resources with a prefix such as /static, then you could target only your static files like this:
app.use('/static', express.static('/public'));
And, you could run the middleware only for the static files like this:
app.use('/static', (req, res, next) => { ... });
app.use('/static', express.static('/public'));
Request. Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.
A simple request example could look like:
var request = require('request');
request({
url: "https://some.url.com",
method: "POST",
headers: { "content-type" : "application/json" },
json: payload
},
function (error, response, data) {
if (!error && response.statusCode == 200) {
//handle request
}
else {
callback({ "errorMessage": "There was an error with the request" });
}
}
);
Ok, first: caching. Define a variable or a module that will be accessible to your future middleware:
./cache.js
const cache = {};
module.exports = cache;
This module will hold users' data, like:
{
'id00001': {
email: 'foo#bar.com',
birthday: 1488700668567
},
'id00002': {
email: 'baz#bar.com',
birthday: 1488700668590
},
// etc...
}
Next, use Request and Express middleware:
./app.js
const express = require('express'),
app = express();
const cache = require('./cache'),
request = require('request');
const getUserDataMiddleware = (req, res, next) => {
// assuming you have a mechanism to identify your users already somehow
let user = req.user,
id = user.id;
// if data is not in cache
if (!cache[id]) {
request.get({
url: 'http://getuserdata.com/' + id,
json: true
}, (err, response, body) => {
if (err) {
next(err);
return;
}
// save received data to cache
cache[id] = body;
next();
});
} else {
// you have your user data in cache, do whatever you want now
next();
}
};
app.use('/public', getUserDataMiddleware, express.static('./public'));

How to handle 403 HTTP error in restify?

I've been trying to handle HTTP error in restify. these are the codes i've been trying so far :
function send403(req, res, err, cb) {
console.log('Forbidden');
res.end();
}
And then i use that function in ForbiddenError event :
server.on('ForbiddenError', send403);
I'm expecting that function to be executed everytime server receive a request without authorization header
server.get('/resource', function (req, res, next) {
if(typeof req.headers['authorization'] === 'undefined') {
return next(new restify.ForbiddenError());
}
else {
// HTTP 200
}
});
Everytime i try to access the url i keep getting that default restify JSON message.
FYI i've tried the same method for 404 error and it works
server.on('NotFound', send404);
Everytime i try to send a request to a non-existing url, the send404 function will be executed.
I think this might be what you are looking for. I noticed that in the "server.on" statement restify doesn't want the "Error" part. The following is a complete working example.
'use strict';
var restify = require('restify');
var server = restify.createServer();
server.on('Forbidden', send403);
server.get('/resource', function(req, res, next){
if(typeof req.headers['authorization'] === 'undefined') {
next(new restify.ForbiddenError());
}
else {
// HTTP 200
}
});
function send403(req, res, err, cb){
console.log('Log something here');
return cb();
}
server.listen(9000);
console.log('listening on port 9000');

Resources