I am a relative newbie of Node.js. It been two days that I am trying to modify the body of a Request in Node.js and then forwarding it. For proxying I am using http-proxy module.
What I have to do is to intercept the password of a user inside a JSON object, encrypting it and set the new encrypted password inside the request body.
The problem is that every time I try to collect the request body I consume it (i.e. using body-parser). How can I accomplish this task? I know that the Request in node is seen has a stream.
For sake o completeness, I am using express to chain multiple operation before proxying.
EDIT
The fact that I have to proxy the request is not useless. It follows the code that I am trying to use.
function encipher(req, res, next){
var password = req.body.password;
var encryptionData = Crypto().saltHashPassword(password);
req.body.password = encryptionData.passwordHash;
req.body['salt'] = encryptionData.salt;
next();
}
server.post("/users", bodyParser.json(), encipher, function(req, res) {
apiProxy.web(req, res, {target: apiUserForwardingUrl});
});
The server (REST made by Spring MVC) give me the exception Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: null
The real problem is that there is an integration problem between modules body-parser and http-proxy, as stated in this thread.
One solution is to configure body-parser after http-proxy. If you can't change the order of the middleware (as in my case), you can restream the parsed body before proxying the request.
// restream parsed body before proxying
proxy.on('proxyReq', function(proxyReq, req, res, options) {
if (req.body) {
let bodyData = JSON.stringify(req.body);
// if content-type is application/x-www-form-urlencoded -> we need to change to application/json
proxyReq.setHeader('Content-Type','application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
// stream the content
proxyReq.write(bodyData);
}
}
Why don't use express chaining for this ?
In your first function just do something like this :
req.body.password = encrypt(req.body.password); next();
You just have to use a middleware.
body-parser is also just a middleware that parses the request bodies and puts it under req.body
You can do something like this:
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
function encryptPassword(req, res, next) {
req.body.password = encrypt(req.body.password);
// You can do anything really here and modify the req
//call next after you are done to pass on to next function
next();
}
app.use(encryptPassword);
Generally people use middlewares for authentication, role-based access control etc.....
You can use middlewares in particular routes also:
app.post('/password', encryptPassword, function(req, res) {
// Here the req.body.password is the encrypted password....
// You can do other operations related to this endpoint, like store password in database
return res.status(201).send("Password updated!!");
});
Related
I cant access body when getting expressjs request
My main server.js file
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(router);
My router.js file
const express = require("express");
const articlesContr = require("./../controllers/articlesContr.js");
const router = express.Router();
router
.get("/articles", articlesContr.handleGet)
.post("/articles", articlesContr.handlePost)
.put("/articles", articlesContr.handleUpdate)
.delete("/articles", articlesContr.handleDelete);
adminPanelContr.js
const controller = {
handleGet: (req, res) => {
const query = req._parsedUrl.query;
const parsedQuery = querystring.parse(query);
// res.send(productsDb.getItem(parsedQuery));
console.log(req)
res.send(JSON.stringify(req.body))
}
};
GET requests don't have body parameters. Ordinarily .get() handler functions don't look for them.
If you use the same handler function for GET and other (POST, PUT, PATCH, DELETE) requests, you need logic to avoid looking at the body when you get a GET.
When you send a request with a JSON body, don't forget to tell your server it's JSON by including a Content-Type: application/json HTTP header. That way you're sure express will invoke its express.json middleware to parse the body into the req.body object.
When you .get() a GET, you don't get a request body. There's gotta be some sort of doggerel poetry in there someplace.
If you use res.json(object) rather than res.send(JSON.stringify(object)) express does a good job of setting the HTTP response headers to useful values for JSON.
And, I'm sure you know this: Responding to a request with its own request body is a little strange.
I am creating a PUT method to update a given set of data however in my update function, req.body is undefined.
controller.js
async function reviewExists(req, res, next) {
const message = { error: `Review cannot be found.` };
const { reviewId } = req.params;
if (!reviewId) return next(message);
let review = await ReviewsService.getReviewById(reviewId);
if (!review) {
return res.status(404).json(message);
}
res.locals.review = review;
next();
}
async function update(req, res, next) {
console.log(req.body);
const knexInstance = req.app.get('db');
const {
review: { review_id: reviewId, ...review },
} = res.locals;
const updatedReview = { ...review, ...req.body.data };
const newReview = await ReviewsService.updateReview(
reviewId,
updatedReview,
knexInstance
);
res.json({ data: newReview });
}
service.js
const getReviewById = (reviewId) =>
knex('reviews').select('*').where({ review_id: reviewId }).first();
const updateReview = (reviewId, updatedReview) =>
knex('reviews')
.select('*')
.where({ review_id: reviewId })
.update(updatedReview, '*');
How it should look:
"data": {
"review_id": 1,
"content": "New content...",
"score": 3,
"created_at": "2021-02-23T20:48:13.315Z",
"updated_at": "2021-02-23T20:48:13.315Z",
"critic_id": 1,
"movie_id": 1,
"critic": {
"critic_id": 1,
"preferred_name": "Chana",
"surname": "Gibson",
"organization_name": "Film Frenzy",
"created_at": "2021-02-23T20:48:13.308Z",
"updated_at": "2021-02-23T20:48:13.308Z"
}
The first function where I check if a review exists work with my delete method. Am I missing something in one of these functions that would make req.body undefined?
Nobody in the other answers is really explaining the "why" here. By default req.body is undefined or empty and the Express engine does not read the body of the incoming request.
So, if you want to know what's in the body of the request and, even further, if you want it read and then parsed into req.body so you can directly access it there, then you need to install the appropriate middleware that will see what type of request it is and if that's the type of request that has a body (like a POST or a PUT) and then it will look at the incoming content-type and see if it's a content-type that it knows how to parse, then that middleware will read the body of the request, parse it and put the parsed results into req.body so it's there when your request handler gets called. If you don't have this type of middleware installed, then req.body will be undefined or empty.
Express has middleware like this built in for several content-types. You can read about them here in the Express doc. There is middleware for the following content types:
express.json(...) for "application/json"
express.raw(...) reads the body into a Buffer for you to parse yourself
express.text(...) for "text/plain" - reads the body into a string
express.urlencoded(...) for "application/x-www-form-urlencoded"
So, you will need some middleware to parse your specific content so you can access it in req.body. You don't say exactly data type you're using, but from your data expectations, I would guess that perhaps it's JSON. For that, you would place this:
app.use(express.json());
Somewhere before your PUT request handler. If your data format is something else, then you could use one of the other built-in middleware options.
Note, for other data types, such as file uploads and such, there are whole modules such as multer for reading and parsing those data types into properties that your request handler can offer.
You should use a body parser. Basically, Express can't know how to parse the incoming data the users throw at it, so you need to parse the data for it. Luckily, there are a few body parsers (and for quite some time body parsers for JSON and UrlEncoded (not sure if there are any others) are built into Express itself). The way you do that is you add a middleware in your express app like that:
const app = express();
...
app.use(express.json({
type: "*/*" // optional, only if you want to be sure that everything is parsed as JSON. Wouldn't recommend
}));
...
// the rest of the express app
Make sure that you have body-parser middleware installed, which extract the body of request stream and exposes it on req.body (source)
npm install body-parser --save
Here's how express server definitions would look like:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
To get access to request body, you need to use express.json(). Make sure before using router, just wire your app.use(express.json()) that should make the request object available.
little help?
I am trying to write pure JSON to a file inside my project, my project tree looks like this:
src
->app
-->components
--->people.component.ts
--->(other irrelevant components)
-->services
--->people.service.ts
-->data
--->JSON.json
->(other irrelevant src files)
The code in my people.component.ts is just used to call and subscribe to a function inside my people.service.ts, passing the newPerson property which is data-binded from the DOM using angular2 magic; this is the function inside name.service.ts:
public addPerson(newPerson) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post('app/data/PEOPLE.json', newPerson, { header: headers })
.map(res => res.json())
}
My objective is to write (if necessary replace) the newPerson property (or the entire file) inside PEOPLE.json. of course, the current addPerson() function returns an error.
I've also tried the put method, it errors slightly differently, but i haven't found solutions to either method.
I know categorically it's not an error with the format/type of data i'm trying to put in PEOPLE.json file.
Unfortunately, you can not PUT or POST directly to a file on your local filesystem using client side (browser) JavaScript.
Consider building a simple server to handle a POST request. If you are most comfortable with JavaScript, try Express with Node.js
// server.js
'use strict'
const bodyParser = require('body-parser');
const express = require('express');
const fs = require('fs');
const app = express()
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:8000');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'POST');
next();
});
app.post('/', (req, res) => {
console.log('Received request');
fs.writeFile('json.json', JSON.stringify(req.body), (err) => {
if (err) throw err;
console.log('File written to JSON.json');
res.send('File written to JSON.json')
})
});
app.listen(3000, ()=>{
console.log('Listening on port 3000. Post a file to http://localhost:3000 to save to /JSON.json');
});
You cannot write to files from Browser. Period. Even if such action is possible it will be stored on User side, not your server. If your task is indeed to write something on user end, please refer to localStorage documentation (or couple other API implementations). But if you are going to user that file for some purpose outside of your browser, it will be non-trivial task.
If you want to save file on server, then you need to hande it on back end.
I am working with a single app application framework called reactjs, the issue I encountered is setting httpOnly cookies, as they can not be set / read from a client side I needed to figure out a way how to use express for this.
One idea I came up with is to make a post request to a route like /cookie:data where data is value of token that needs to be stored in a cookie, so:
app.post('/cookie:data', function(req, res) {
// Set cookie here
res.send(200)
})
Issue I am hesitant about is that token contains unique user identifier that is used to secure api, and I am not sure if I am or am not exposing this by setting up a cookie this way.
Alternatively instead of using :data it would be beneficial to figure out how I can grab data (json object) from the post request
EDIT:
One issue I can think of is that anyone can post to this route and set different cookies? what would be a way of securing it?
EDIT 2:
This is the express setup I use to proxy api calls (only relevant for clarifying comments)
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
Say that you want to proxy all requests starting with /api to a third-party, except /api/users, which you want to perform 'manually' because it returns a token you need:
app.post('/api/users', function(req, res) {
let url = config.API_HOST + req.url;
let apiRequest = request.post(url, function(err, response, body) {
// Responses are just examples, you should tailor them to your situation
if (err) {
return res.sendStatus(500);
} else if (response.statusCode !== 200) {
return res.sendStatus(response.statusCode);
} else {
res.cookie('token', body).send('OK');
}
});
req.pipe(apiRequest);
})
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
Being a newbie in Nodejs, I jumped right into writing a simple app without really reading up on good security practices. I just found out that using bodyParser() for all routes is actually a bad thing because it allows for DOS attack using multipart files.
A recommended fix is to only load specific modules depending on the route. ie, for multipart fileupload, use multipart. For regular POST without file uploads (ie, text form submission), use express.json(), express.urlencoded().
Or another option is to use busboy with connect-busboy. But the thing I'm confused on is how I can specify which route should handle multipart data and which should not? Otherwise, wouldn't I have the same problem as with bodyParser?
Furthermore, busboy docs says it does not handle GET:
If you find that req.busboy is not defined in your code when you expect it to be, check that the following conditions are met. If they are not, req.busboy won't be defined:
1. The request method is not GET or HEAD
So, I'm even more confused how I would parse params in a GET. I think bodyParser does this for me so I could access data with req.params.
For example, how would I migrate away from bodyParser() to busboy/connect-busboy with this simple app:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var busboy = require('connect-busboy');
app.use(busboy());
// How to use busboy to prevent multipart files here?
app.post("/form_data_no_fileupload", function(req, res) {
var somedata = req.body.somedata;
});
// Use busboy to handle both regular form data + fileuploads
app.post("/form_data_AND_fileupload", function(req, res) {
});
// What would handle GET without bodyparser?
app.get("/get_something", function(req, res) {
var params = req.params;
});
http.listen(3000, function() {});
[How] I can specify which route should handle multipart data and which should not?
All of Express' routing methods allow for providing middleware specific to the route. This includes Router methods.
app.METHOD(path, callback [, callback ...])
Depending on the body expected for an individual route, you can use different modules to handle each of them (rather than applying them to the entire application with app.use()).
var express = require('express');
var app = express();
var http = require('http').Server(app);
var bodyParser = require('body-parser');
var busboy = require('connect-busboy');
app.post("/form_data_no_fileupload",
bodyParser.urlencoded(),
function(req, res, next) {
// check that the request's body was as expected
if (!req.body) return next('route'); // or next(new Error('...'));
// ...
});
app.post("/form_data_AND_fileupload",
busboy({
limits: {
fileSize: 10 * 1024 * 1024
}
}),
function(req, res, next) {
// check that the request's body was as expected
if (!req.busboy) return next('route'); // or next(new Error('...'));
// ...
});
// ...
Furthermore, busboy docs says it does not handle GET.
So, I'm even more confused how I would parse params in a GET.
Busboy and BodyParser are designed for reading in and parsing the request's body, which GET and HEAD requests aren't expected to have.
For such requests, parameters can only be passed within the query-string within the URL, which Express parses itself. They're available via req.query.
app.get('/get_something', function () {
console.log(req.originalUrl);
// "/get_something?id=1
console.log(req.query);
// { id: "1" }
});
req.params represents any placeholders matched in the path by the route. These are available for any route, regardless of the method.
app.get('/thing/:id', function (req, res) {
console.log(req.originalUrl);
// "/thing/2"
console.log(req.params);
// { id: "2" }
});