Node.js send post with array parameter - javascript

Hello and happy new year!
I'm trying to send a post petition in Node.js. The version I need to replicate in javascript looks like this:
$.post('website.com/api/buy.php', {
action: order,
'items[]': cart_i,
time: localtime,
utb: x //I don't know why that parameter is sent. It's always 0 ._.
} ... //rest of the code of that website
I'm trying to do this in Node.js to replicate that:
var postData = querystring.stringify({
action: 'order',
"items[]": item.i,
time: local_time,
utb: 0
});
var https = require('https');
var options = { method: 'POST', host: 'website.com', port: 443, path: '/api/buy.php', headers: { 'Cache-Control': 'no-cache', 'Cookie': cookies, 'Accept': '/', 'Connection': 'keep-alive' } };
var req = https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
content = content+ chunk;
});
res.on('end', function () {
console.log(content)
});
}
);
req.write(postData);
req.end();
But I always get an error telling me that I didnt send anything. I think the problem could be in items[] because I see it sends "items%5B%5D", but I dont know if I'm doing something wrong. I think is is not about cookies, because if I'm not logged I get other error.
I'm doing the request well? Or I'm missing something that is not about the request? Is there other way to send "post" data easier?
Any help would be appreciated :)

Related

Proxying an endpoint to avoid CORS issues

I am using an external api that doesn't allow client side POST request. I can make a POST request using node.js and I am getting my desired response on the server. I am stuck trying to figure out how to get the response from the server into my HTML file.
const https = require("https");
const data = JSON.stringify({
key: "value"
});
const options = {
hostname: "url",
port: 443,
path: "/path",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": data.length
}
};
const req = https.request(options, res => {
console.log(
`statusCode: ${res.statusCode} statusMessage: ${res.statusMessage}`
);
res.setEncoding("utf8");
res.on("data", chunk => {
console.log(chunk);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data)
req.end();
This is my server.js file, I'm not sure what the next step is to get in a file.

Add parameters to HTTP POST request in Node.JS

I've known the way to send a simple HTTP request using Node.js as the following:
var http = require('http');
var options = {
host: 'example.com',
port: 80,
path: '/foo.html'
};
http.get(options, function(resp){
resp.on('data', function(chunk){
//do something with chunk
});
}).on("error", function(e){
console.log("Got error: " + e.message);
});
I want to know how to embed parameters in the body of POST request and how to capture them from the receiver module.
Would you mind using the request library. Sending a post request becomes as simple as
var options = {
url: 'https://someurl.com',
'method': 'POST',
'body': {"key":"val"}
};
request(options,function(error,response,body){
//do what you want with this callback functon
});
The request library also has a shortcut for post in request.post method in which you pass the url to make a post request to along with the data to send to that url.
Edit based on comment
To "capture" a post request it would be best if you used some kind of framework. Since express is the most popular one I will give an example of express. In case you are not familiar with express I suggest reading a getting started guide by the author himself.
All you need to do is create a post route and the callback function will contain the data that is posted to that url
app.post('/name-of-route',function(req,res){
console.log(req.body);
//req.body contains the post data that you posted to the url
});
If you want to use the native http module, parameters can be included in body this way:
var http = require('follow-redirects').http;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'example.com',
'path': '/foo.html',
'headers': {
},
'maxRedirects': 20
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"examplekey\"\r\n\r\nexamplevalue\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--";
req.setHeader('content-type', 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW');
req.write(postData);
req.end();

Passing CSRF token from Node.js to Django

I'm working on a simple multiplayer word game app in Django (1.5). Following the example here, I'm using a separate Node.js server and Socket.io to manage the client connections.
My question breaks down into two parts:
The tutorial above uses #csrf_exempt for the API view. As the POST does not come from the client, but from the Node.js server over localhost, what exactly am I exposed to by not using CSRF protection for this view?
As I'm not sure of the above, I would like to use the CSRF protection. I have tried to extract the CSRF token from the cookie supplied by Django (as suggested by the docs) and send it along with the POST, but I still get a 403 response.
game_server.js:
io.configure(function () {
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = cookie_reader.parse(data.headers.cookie);
return accept(null, true);
}
return accept('error', false);
});
io.set('log level', 1);
});
io.sockets.on('connection', function (socket) {
socket.on('check_word', function (data) {
values = querystring.stringify({
word: data,
sessionid: socket.handshake.cookie['sessionid']
});
var options = {
host: 'localhost',
port: 8000,
path: '/node/check_word',
method: 'POST',
headers: {
'X-CSRFToken': socket.handshake.cookie['csrftoken'],
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': values.length
}
};
var req = http.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (message) {
if (message) {
console.log(message);
}
});
});
req.write(values);
req.end();
});
});
game.html (script portion only):
(function ($) {
var socket = io.connect('localhost', { port: 4000 });
socket.on('connect', function () {
console.log("connected");
});
word_el = $('#word-input');
word_el.keypress(function (event) {
if (event.keyCode === 13) {
// Enter key pressed
var word = word_el.attr('value');
if (word) {
socket.emit('check_word', word, function (data) {
console.log(data);
});
}
word_el.attr('value', '');
}
});
})(jQuery);
views.py:
#ensure_csrf_cookie
def check_word(request):
return HttpResponse("MATCH:" + request.POST.get('word'))
Any insight would be greatly appreciated!
After a fair bit of research and experimentation, I've solved the problem.
My findings:
In this particular case, CSRF doesn't expose me to any meaningful attacks. In theory it opens a route for cheating in the game, but that's a lot of difficulty (requiring fabrication of session id and targeting a game currently in progress) for zero reward. However, in other applications such as chat, a CSRF vulnerability here allows for someone to impersonate another user, which is a more important concern. And so we dig deeper...
My original attempt to solve the problem via AJAX headers was a mistake. For one, the request is not actually coming over AJAX. (request.is_ajax() returns False within the view.) Secondly, the error page received from Django cites CSRF cookie not set as the reason for failure.
All of which builds up to the solution:
var options = {
// snip...
headers: {
'Cookie': 'csrftoken=' + socket.handshake.cookie['csrftoken'],
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': values.length
}
};
Add the proper 'Cookie' header, and the request succeeds.

Node.js Requests returning 301 redirects

I'm brand new to node.js, but I wanted to play around with some basic code and make a few requests. At the moment, I'm playing around with the OCW search (http://www.ocwsearch.com/), and I'm trying to make a few basic requests using their sample search request:
However, no matter what request I try to make (even if I just query google.com), it's returning me
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/0.7.65</center>
</body>
</html>
I'm not too sure what's going on. I've looked up nginx, but most questions asked about it seemed to be asked by people who were setting up their own servers. I've tried using an https request instead, but that returns an error 'ENOTFOUND'.
My code below:
var http = require('http');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\n');
var options = {
host:'ocwsearch.com',
path:
'/api/v1/search.json?q=statistics&contact=http%3a%2f%2fwww.ocwsearch.com%2fabout/',
method: 'GET'
}
var req = http.request(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
res.on('data', function(d) {
process.stdout.write(d);
});
});
req.end();
req.on('error', function(e) {
console.error(e);
});
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
Sorry if this is a really simple question, and thanks for any help you can give!
For me the website I was trying to GET was redirecting me to the secure protocol. So I changed
require('http');
to
require('https');
The problem is that Node.JS's HTTP Request module isn't following the redirect you are given.
See this question for more: How do you follow an HTTP Redirect in Node.js?
Basically, you can either look through the headers and handle the redirect yourself, or use one of the handful of modules for this. I've used the "request" library, and have had good luck with it myself. https://github.com/mikeal/request
var http = require('http');
var find_link = function(link, callback){
var root ='';
var f = function(link){
http.get(link, function(res) {
if (res.statusCode == 301) {
f(res.headers.location);
} else {
callback(link);
}
});
}
f(link, function(t){i(t,'*')});
}
find_link('http://somelink.com/mJLsASAK',function(link){
console.log(link);
});
function i(data){
console.log( require('util').inspect(data,{depth:null,colors:true}) )
}
This question is old now, but I got the same 301 error and these answers didn't actually help me to solve the problem.
I wrote the same code:
var options = {
hostname: 'google.com',
port: 80,
path: '/',
method: 'GET',
headers: {
'Content-Type': 'text/plain',
}
};
var http = require('http');
var req = http.request(options, function(res) {
console.log('STATUS:',res.statusCode);
console.log('HEADERS: ', JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function(chunk) {
console.log(chunk);
});
res.on('end', function() {
console.log('No more data in response.');
});
});
req.on('error', function(e) {
console.log('problem with request: ', e.message);
});
console.log(req);
req.end();
so after some time I realized that there's a really tiny mistake in this code which is hostname part:
var options = {
hostname: 'google.com',
...
you have to add "www." before your URL to get html content, otherwise there would be 301 error.
var options = {
hostname: 'www.google.com',

How to post to a request using node.js

I am trying to post some json to a URL. I saw various other questions about this on stackoverflow but none of them seemed to be clear or work. This is how far I got, I modified the example on the api docs:
var http = require('http');
var google = http.createClient(80, 'server');
var request = google.request('POST', '/get_stuff',
{'host': 'sever', 'content-type': 'application/json'});
request.write(JSON.stringify(some_json),encoding='utf8'); //possibly need to escape as well?
request.end();
request.on('response', function (response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
When I post this to the server I get an error telling me that it's not of the json format or that it's not utf8, which they should be. I tried to pull the request url but it is null. I am just starting with nodejs so please be nice.
The issue is that you are setting Content-Type in the wrong place. It is part of the request headers, which have their own key in the options object, the first parameter of the request() method. Here's an implementation using ClientRequest() for a one-time transaction (you can keep createClient() if you need to make multiple connections to the same server):
var http = require('http')
var body = JSON.stringify({
foo: "bar"
})
var request = new http.ClientRequest({
hostname: "SERVER_NAME",
port: 80,
path: "/get_stuff",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body)
}
})
request.end(body)
The rest of the code in the question is correct (request.on() and below).
Jammus got this right. If the Content-Length header is not set, then the body will contain some kind of length at the start and a 0 at the end.
So when I was sending from Node:
{"email":"joe#bloggs.com","passwd":"123456"}
my rails server was receiving:
"2b {"email":"joe#bloggs.com","passwd":"123456"} 0 "
Rails didn't understand the 2b, so it wouldn't interpret the results.
So, for passing params via JSON, set the Content-Type to application/json, and always give the Content-Length.
To send JSON as POST to an external API with NodeJS... (and "http" module)
var http = require('http');
var post_req = null,
post_data = '{"login":"toto","password":"okay","duration":"9999"}';
var post_options = {
hostname: '192.168.1.1',
port : '8080',
path : '/web/authenticate',
method : 'POST',
headers : {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Content-Length': post_data.length
}
};
post_req = http.request(post_options, function (res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ', chunk);
});
});
post_req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
post_req.write(post_data);
post_req.end();
There is a very good library that support sending POST request in Nodejs:
Link: https://github.com/mikeal/request
Sample code:
var request = require('request');
//test data
var USER_DATA = {
"email": "email#mail.com",
"password": "a075d17f3d453073853f813838c15b8023b8c487038436354fe599c3942e1f95"
}
var options = {
method: 'POST',
url: 'URL:PORT/PATH',
headers: {
'Content-Type': 'application/json'
},
json: USER_DATA
};
function callback(error, response, body) {
if (!error) {
var info = JSON.parse(JSON.stringify(body));
console.log(info);
}
else {
console.log('Error happened: '+ error);
}
}
//send request
request(options, callback);
Try including the content length.
var body = JSON.stringify(some_json);
var request = google.request('POST', '/get_stuff', {
host: 'server',
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'application/json'
});
request.write(body);
request.end();
This might not solve your problem, but javascript doesn't support named arguments, so where you say:
request.write(JSON.stringify(some_json),encoding='utf8');
You should be saying:
request.write(JSON.stringify(some_json),'utf8');
The encoding= is assigning to a global variable, so it's valid syntax but probably not doing what you intend.
Probably non-existent at the time this question was asked, you could use nowadays a higher level library for handling http requests, such as https://github.com/mikeal/request. Node's built-in http module is too low level for beginners to start with.
Mikeal's request module has built-in support for directly handling JSON (see the documentation, especially https://github.com/mikeal/request#requestoptions-callback).
var request = google.request(
'POST',
'/get_stuff',
{
'host': 'sever',
**'headers'**:
{
'content-type': 'application/json'
}
}
);

Categories