modify response in express/nodejs - node.js

i am making multiple streamdata in Nodejs using express.
this is how i make a url:
app.get('/temp/1', function(req, res){
res.send('hello, i am not modified')
})
my question is: is it possible to modify the response of that url?
i tried like this:
app.get(/modify/1, function(req, res){
app.get('/temp/1', function(req, res){
res.send('hello, i am modified')
})
res.send('done');
}
So i would think that the response is changed, but nothing happens actually.
is there a way to achieve it?

Here's an example using express-modify-response:
const modifyResponse = require('express-modify-response');
...
let modify = modifyResponse(
function(req, res) { return true }, // always modify the response
function(req, res, body) { return 'hello, i am modified' } // the new response
);
app.get('/temp/1', modify, function(req, res){
res.send('hello, i am not modified')
})
EDIT: second attempt. You have an endpoint /temp/1 that sends a particular response, and you want an endpoint /modify/1 that will take that response and modify it.
This calls for some abstraction:
function someFunction(id) {
return 'hello, i am not modified';
}
app.get('/temp/1', function(req, res) {
res.send(someFunction(1));
});
app.get('/modify/1', function(req, res) {
let value = someFunction(1);
// Remove the word `not`.
value = value.replace(/not /, '');
res.send(value);
});
So both handlers use the same function, that provides the actual output, but /modify/1 modifies the output before returning it to the client.

Related

Is it possible to use multiple .get on app.route?

app.route('/users')
.post(user.post)
.get(user.get)
.get(user.everyone)
.put(user.update)
.delete(user.delete);
I have ran into the problem of my function using two res.send, so I am getting the 'Error: cannot set header after they are sent.' error, to fix this I have turned it into two functions which I am trying to use two .get on the app.route, but it seems I can only use one as when I use two the second one doesn't work.
Is there a way I could use two .get on one app.route?
If not, what are my options to get around this problem?
You need to create separate routes for each api endpoint like this:
app.route('/users').get(req, res) => {
//Get all users
});
app.route('/users:/id').get(req, res) => {
//Get specific user
});
app.route('/users').post(req, res) => {
//Create new user
});
app.route('/users/:id').put(req, res) => {
//Update user
});
app.route('/users/:id').delete(req, res) => {
//Delete user
});
Once res.send is called, means your server has sent the response to the browser or whatever. you can't change the already sent response and its header.
You can use multiple callbacks on one route and one method(post,get)
An array of callback functions can handle a route. For example:
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
var cb2 = function (req, res) {
res.send('Hello from C!')
}
app.get('/example/c', [cb0, cb1, cb2])
yes, you can use multiple HTTP requests either its .get or .post but with different params. or routes.

ExpressJS Handle multiple get route with the same endpoint

I build an application API with expressJS. In this application there is a public part and an private part.
Both part need to display a list of user. But only the private part can display the full list.
I use express-authorize middlewar to handle autorization. So i have defined two authorization :
'public:users:list'
and
'private:users:list'
In express i have tried to write routes like this :
router.route('/users')
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
})
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
But this does not work. Request fail as unauthorized on the first get and never goes to the second. Is there a mean to make this works? Or any others techniques. If possible avoid to create another endpoint like (router.route('usersPublic').
Thx
EDIT:
I have override the onDenied function like this and make the change in the route.
var authorizer = new authorizer.Authorizer(authorizer.options);
authorizer.options.onDenied = function (req, res, next) { next('route') };
authorizer.options.withSubject = function (req, res, done) {}
That seems to work. But if no route are valid. i get a 404 error instead of 403. Any suggestion?
EDIT2:
onDenied should not be set at global but inside the route itself like this
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.onDenied((req, res, next) => next('route')).isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
});
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
I'm not familiar with express-authorize, so the following is pieced together from its source and may not work.
First, you need to change how authentication mismatches are handled. The default is to redirect to /login, but you need to change that so the request gets passed to the next route chain:
let myAuthorizer = authorization.authorizer.onDenied((req, res, next) => next('route'));
Then you need to set up two separate routes for /users:
router.route('/users').get([passport.authenticate('jwt', { session: false }), myAuthorizer.isPermitted('private:users:list') ], (req, res) => {
getUserList(req, res);
})
router.route('/users').get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
The next('route') is explained here, but it basically means that instead of passing the request to the next handler in the current chain, it's passed to the next full route chain (which in this case is the second router.route('/users') if private access is denied).

node http-proxy: async modification of request body

I need to modify the request body asynchronously. Something along the lines of this:
proxy.on('proxyReq', function(proxyReq, req, res, options) {
if(req.body) {
new Promise(function(resolve){
setTimeout(function() { // wait for the db to return
'use strict';
req.body.text += 'test';
let bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type','application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
// stream the content
proxyReq.write(bodyData);
resolve();
},1);
});
}
});
When I run this I get the error saying cannot modfiy headers once they have been set. Which makes sense.
How can I halt the sending of the request until I'm ready? I've looked at removing various listeners from proxyReq without success..
By looking at the source code #-) it seems like it's not really possible because the proxyReq event is sent and then the code moves on.
If it would instead wait for a promise, it would be possible (if you'd return that promise as well).
A minimal fork on this lib could be for example:
// Enable developers to modify the proxyReq before headers are sent
proxyReq.on('socket', function(socket) {
if(server) { server.emit('proxyReq', proxyReq, req, res, options); }
});
(proxyReq.proxyWait || Promise.resolve())
.then( ... // rest of the code inside the callback
And then
proxy.on('proxyReq', function(proxyReq, req, res, options) {
if(req.body) {
proxyReq.proxyWait = new Promise(function(resolve){
setTimeout(function() { ...
But depending on your use case, there might be other solutions as well. For example, consider if it's really necessary that you use this proxy library. It You could alternatively use http directly, where you have all the control on the events and callbacks.
You can set selfHandleResponse: true inside the HttpProxy.createProxyServer. This then allows (and forces) you to handle the proxyRes manually!
const proxy = HttpProxy.createProxyServer({selfHandleResponse: true});
proxy.on('proxyRes', async (proxyReq, req, res, options) => {
if (proxyReq.statusCode === 404) {
req.logger.debug('Proxy Request Returned 404');
const something = await doSomething(proxyReq);
return res.json(something);
}
return x;// return original proxy response
});
I came here looking for the solution to a slightly different problem: Modifying the request headers (not body) before proxying.
I post this here in case that it is helpful to others. And maybe the code can be adapted to also modify the request body.
const http = require('http');
const httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});
var server = http.createServer(function(req, res) {
console.log(`${req.url} - sleeping 1s...`);
setTimeout(() => {
console.log(`${req.url} - processing request`);
req.headers['x-example-req-async'] = '456';
proxy.web(req, res, {
target: 'http://127.0.0.1:80'
});
}, 1000);
});
server.listen(5050);

Changing the header of the response on NodeJS using request

I have the following problem:
I want to get a static file from another server, and give it back to the user with another content-type header.
The following code works just fine, but I can't figure out a way to change the response header, though.
const request = require('request');
app.get('video', function (req, res) {
request.get('http://anotherurl.com/video-sample.mp4').pipe(res);
});
I tried to do this thing more manually, but the response was very slow.
app.get('video', function (req, res) {
request.get('http://anotherurl.com/video-sample.mp4', function(error, response, body) {
// ...
res.setHeader('content-type', 'image/png');
res.send(new Buffer(body));
});
});
Can you guys help me with that?
Thanks
Just set the response header when the 'response' event fires.
app.get('video', (req, res) => {
request.get('http://anotherurl.com/video-sample.mp4')
.on('response', response => {
res.setHeader('Content-Type', 'image/png');
// pipe response to res
// since response is an http.IncomingMessage
response.pipe(res);
});
});

Express logging with storing the request information

Background
Yes, there are a lot of different Node.js logging library winston, bunyan and console.log. It's easy to log down the information of the specific request when it has called and when and what information would be in response.
The problem
The problem begins with the sub function calls. When under one request your calling multiple functions which also uses the same logging, how would you pass the request meta - data to these log calls (function parameters seems to be one possible way but these are really messy) ?
Example
Small visual for coders:
// Middleware to set some request based information
app.use(function (req, res, next) {
req.rid = 'Random generated request id for tracking sub queries';
});
app.get('/', function (req, rest) {
async.series({
'users': async.apply(db.users.find),
'posts': async.apply(db.posts.find),
}, function (err, dbRes) {
console.log('API call made ', req.rid)
res.end(dbRes);
});
});
// Now the database functions are in other file but we also need to track down the request id in there
(db.js)
module.exports = {
users: {
find: function () {
console.log('Calling users listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
},
posts: {
find: function () {
console.log('Calling post listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
}
};
You can log your requests with simple conf in your app.js with;
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
However, you need to provide logs for specific functions in your controller.

Resources