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
Related
My client given me requirement to encrypt decrypt all request response. So for all encrypted request we wrote down the express middleware to get decrypted request. which is the simple part but while sending response we also have to encrypt response.
One way to do write common function to encrypt data and call that function from all routes. which is time consuming part because we have more than 50+ routes in project. So i was thinking to write middleware like we have done for request which capture response before we send and then we encrypt response after that we send encrypt response to client.
I have searched for solution in google not got any proper solution which worked for me.
routes.js
router.post('/getUserData', verifyApiKey, async function (req, res, next) {
let user = await getUserData();
res.status(200).send(user)
});
middlware.js
class EncryptDecryptRequestResponse {
async encryptResponse(req, res, next) {
console.log('Called encryptResponse');
console.log('res.body', res.body);
res.body = encryptData(res.body)
next();
}
}
App.js
// Middleware to decrypt request
app.use(decryptRequest);
app.use('/', indexRouter);
// Middleware to encrypt response
app.use(encryptResponse);
but the problem is that i am not getting any console.log from middleware. this is the solution which i used
I tried to reproduce the problem you're having with overwriting res.send(), but it works fine for me. You need to make sure to setup the interceptor middleware before you define your routes. Consider this simple example:
const express = require('express');
const app = express();
function encryptResponseInterceptor(req, res, next) {
const originalSend = res.send;
res.send = function () {
arguments[0] = encryptResponse(arguments[0]);
originalSend.apply(res, arguments);
};
next();
}
function encryptResponse(originalData) {
// place your encryption logic here, I'm just adding a string in this example
return originalData + " modified";
}
// fake method that returns resolves after 1s just for testing
function getUserData() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000)
})
}
app.use(encryptResponseInterceptor);
app.get("/test", async (req, res, next) => {
await getUserData();
res.status(200).send("will be changed");
})
app.listen(3000, () => {
console.log("server started on 3000");
});
I am wondering how I can take and do something like return res.status(400).json({message: 'This is my message'}) in a controller/middleware then I want to be able to in a callback or another custom controller do res.render('test', {message}) where the message comes from my controller above. I am using EJS for my view engine which is where the {message} part comes from.
I have looked around online and on SO but have yet to find a good explanation of how to properly do this. I know that without the callback res.render part I can have my API work properly and returns the correct JSON to postman.
You can pass data from a middleware to a handler by putting the data in the req object. The middleware is defined as a function like (req, res, next) => { // Do stuff here}. Inside that you can do req.somePayloadIWantToPass = {hello: 'world'};
In your case, passing the information might look like this
const app = require("express")();
const port = 3000;
// Your middleware
app.use((req, res, next) => {
const payload = { message: "this is my message", statusCode: 400 };
req.payload = payload;
next();
});
// Your request handler
app.get("/", (req, res) => {
const { message, statusCode } = req.payload;
res.status(statusCode).render({ message });
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Notes
You can't use send multiple times for a certain request
Here we defined payload as a hard-coded variable, but you can define a certain logic to derive it from your request's body or parameters
I have two endpoints in a node js app:
app.get('search', myGetController);
app.post('add', myPostController);
For simplicity, let's assume both services have only the following code:
exports.myGetController = function(req, res) {
res.status(404).json({ error: "not found" });
};
I want to have a middleware that is executed after the processing of the controllers, but before they are sent to the browser, so I can add a header based on the body of the response.
// process all responses to add ed25519
app.use(function(req, res, next) {
res.setHeader('CharCount', [How To get Body content]);
next();
});
I have two questions:
First of all, I would like to have all my controllers pass by that middleware after processing.
Second, I would like to access the body content so I can generate a header based on its content.
UPDATE
I have tried the suggested answer someone posted, and it is not working, or I am missing something.
This is what I have (before setting up my routes):
app.use(function(req, res, next) {
const oldResJson = res.json;
res.json = function(body) {
res.setHeader('myToken', generateHeaderBasedOnBody(oldResJson));
oldResJson.call(res, body);
}
next();
});
The response that is being passed to my method is an empty string, even though the response sent by the service is not empty. Am I doing this in the wrong place, or what am I missing?
One solution for this issue would be to override the res.json function like so:
// process all responses to add ed25519
app.use(function(req, res, next) {
const oldResJson = res.json;
res.json = function(body) {
res.setHeader('CharCount', /* Use body here */);
oldResJson.call(res, body);
}
next();
});
By doing this, you don't even need to change your controllers.
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);
PROBLEM
I've been looking for request/response timeouts for Express.js but everything seems to be related to the connection rather than the request/response itself.
If a request is taking a long time, it should be timed out. Obviously this shouldn't happen but even a simple mistake as having a route handler without a call to the callback or without res.send(), the browser will keep waiting for a reply forever.
An empty route handler is a perfect example of this.
app.get('/sessions/', function(req, res, callback){});
FIX
I added the following before app.use(app,router); and it seemed to add the timeout functionality. Does anyone have any experience/opinion on this?
app.use(function(req, res, next){
res.setTimeout(120000, function(){
console.log('Request has timed out.');
res.send(408);
});
next();
});
Note that I've set the timeout to 2 minutes.
There is already a Connect Middleware for Timeout support:
var timeout = express.timeout // express v3 and below
var timeout = require('connect-timeout'); //express v4
app.use(timeout(120000));
app.use(haltOnTimedout);
function haltOnTimedout(req, res, next){
if (!req.timedout) next();
}
If you plan on using the Timeout middleware as a top-level middleware like above, the haltOnTimedOut middleware needs to be the last middleware defined in the stack and is used for catching the timeout event. Thanks #Aichholzer for the update.
Side Note:
Keep in mind that if you roll your own timeout middleware, 4xx status codes are for client errors and 5xx are for server errors. 408s are reserved for when:
The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time.
You don't need other npm modules to do this
var server = app.listen();
server.setTimeout(500000);
inspired by https://github.com/expressjs/express/issues/3330
or
app.use(function(req, res, next){
req.setTimeout(500000, function(){
// call back function is called when request timed out.
});
next();
});
An update if one is using Express 4.2 then the timeout middleware has been removed so need to manually add it with
npm install connect-timeout
and in the code it has to be (Edited as per comment, how to include it in the code)
var timeout = require('connect-timeout');
app.use(timeout('100s'));
In case you would like to use timeout middleware and exclude a specific route:
var timeout = require('connect-timeout');
app.use(timeout('5s')); //set 5s timeout for all requests
app.use('/my_route', function(req, res, next) {
req.clearTimeout(); // clear request timeout
req.setTimeout(20000); //set a 20s timeout for this request
next();
}).get('/my_route', function(req, res) {
//do something that takes a long time
});
If you need to test your api, this solotion can you help.
I used this in middleware to test my frontend.
For exmaple: if you need to test loader in frontend.
const router = require('express').Router();
const { data } = require('./data');
router.get('/api/data', (req, res, next) => {
setTimeout(() => {
res.set('Content-Type', 'application/json')
res.status(200).send(data)
next()
}, 2000)
})
module.exports = router;
request.setTimeout(< time in milliseconds >) does the job
https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback
You can try:
return await new Promise((resolve) =>
setTimeout(() => {
resolve(resp);
}, 3000),
);
In above code, 3000 = 3 sec.
Change it according to your requirement.
I have not tried for very long scenarios though. Let me know the results in comments.
Before you set your routes, add the code:
app.all('*', function(req, res, next) {
setTimeout(function() {
next();
}, 120000); // 120 seconds
});