I am trying to make a http post request towards an external API from my NodeJS application. I tried two different approaches I found on the web, however both failed with different issues. The working curl command that results in the correct request looks like this:
curl -d ' { "auth_token":"XXXXXXXXX",
"hrows": [ {"cols": [ {"value":"Name 0"}, {"value":"Value 0"} ] } ],
"rows": [ {"cols": [ {"value":"Name 1"}, {"value":"Value 1"} ] },
{"cols": [ {"value":"Name 2"}, {"value":"Value 2"} ] },
{"cols": [ {"value":"Name 3"}, {"value":"Value 3"} ] },
{"cols": [ {"value":"Name 4"}, {"value":"Value 4"} ] } ]
}' http://example.com:3030/widgets/alarms
1) Trying to make a request with the request library. This doesn't throw an error in the application, but I get an empty request on the API Server (Yes, the content I want to send is a string)
var request = require('request');
var test = "{\"auth_token\":\"XXXXXXXXXX\", \"hrows\": [ {\"cols\": [{\"value\":\"Loc Nr.\"},{\"value\":\"Address\"},{\"value\":\"Status\"}] } ], \"rows\": [ {\"cols\": [ {\"value\":\"Name 1\"}, {\"value\":\"Value 1\"} ] }, {\"cols\": [ {\"value\":\"Name 2\"}, {\"value\":\"Value 2\"} ] } ]}";
var wid = "alarms";
postRequest(wid,test);
function postRequest(widget, content) {
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(content)
}
var options = {
traditional: true,
url: 'http://example.com:3030/widgets/'+widget,
method: 'POST',
headers: headers,
data: content,
contentType : "application/x-www-form-urlencoded"
}
console.log(options);
request.post(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body)
}
})
}
When I do a tcpdump on the API server, I see that the packet received looks similar to the one received when executing the curl command, except for the field http.content_length_header being set to 0 (And the conrent itself is missing). When I check the debug output of the options variable, it sill looks ok:
{ traditional: true,
url: 'http://example.com:3030/widgets/alarms',
method: 'POST',
headers:
{ 'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': 243 },
data:
'{"auth_token":"XXXXXXXXX", "hrows": [ {"cols": [{"value":"Loc Nr."},{"value":"Address"},{"value":"Status"}] } ], "rows": [ {"cols": [ {"value":"Name 1"}, {"value":"Value 1"} ] },\t{"cols": [ {"value":"Name 2"}, {"value":"Value 2"} ] } ]}',
contentType: 'application/x-www-form-urlencoded' }
As you can see content length as well as data are there but not in the packet received (See attached Screenshots, one from the curl command and one from the NodeJS request, both captured on the API server)
Any idea why this is happening?
2) The second way I tried was from a Stackoverflow Post I found on how to post a string. However that one fails with
Error: connect ECONNREFUSED 127.0.0.1:80
though I don't understand why it wants to bind to port 80 on my localhost, however method #1 would anyway be the preferred one, this is just for completeness (And maybe someone knows a smart answer to this as well
var querystring = require('querystring');
var http = require('http');
var fs = require('fs');
var test = "{\"auth_token\":\"XXXXXXXX\", \"hrows\": [ {\"cols\": [{\"value\":\"Loc Nr.\"},{\"value\":\"Address\"},{\"value\":\"Status\"}] } ], \"rows\": [ {\"cols\": [ {\"value\":\"Name 1\"}, {\"value\":\"Value 1\"} ] }, {\"cols\": [ {\"value\":\"Name 2\"}, {\"value\":\"Value 2\"} ] } ]}";
var wid = "alarms";
postRequest(wid,test);
function postRequest(widget, content) {
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(content)
}
var options = {
url: 'http://example.com:3030/widgets/'+widget,
method: 'POST',
headers: headers,
}
var pdata = querystring.stringify(content);
console.log(pdata)
console.log(Object.prototype.toString.call(pdata));
var post_req = http.request(options, function() {
});
// post the data
post_req.write(pdata);
post_req.end();
}
Thanks for any useful hints (Or a hint how to trigger a dashing job using the dashing API but as far as I see that's not possible so I have to stick with these API calls :) )
It goes wrong in
var options = {
traditional: true,
url: 'http://example.com:3030/widgets/'+widget,
method: 'POST',
headers: headers,
data: content,
contentType : "application/x-www-form-urlencoded"
}
data isn't a valid key on request options, it's either body or form. That would
explain why no body data is being sent in the request.
In your case you want form
form - when passed an object or a querystring, this sets body to a querystring representation of value, and adds Content-type:
application/x-www-form-urlencoded header. When passed no options, a
FormData instance is returned (and is piped to request). See "Forms"
section above.
var options = {
traditional: true,
url: 'http://example.com:3030/widgets/'+widget,
method: 'POST',
headers: headers,
form: content,
contentType : "application/x-www-form-urlencoded"
}
see https://www.npmjs.com/package/request#requestoptions-callback for more details.
Related
I am having trouble setting up Partner Referrals when calling the PayPal API using Node.
Every time I attempt to call the API I receive the following error:
error: "invalid_token"
error_description: "The token passed in was not found in the system"
According to the documentation the URL to call is https://api-m.sandbox.paypal.com/v2/customer/partner-referrals
Looking at the URL and the error message, I believe I am getting this error because I am using production credentials, not sandbox. However, I cannot find any documentation showing the production URL for this.
Am I correct in believing this is the sandbox URL? What is the production URL if so?
Ive followed the onboarding checklist but cant seem to make this work.
Here is my code:
getAuthToken = async () => {
const clientIdAndSecret = "mylongsecret";
const authUrl = "https://api-m.paypal.com/v1/oauth2/token";
const base64 = Buffer.from(clientIdAndSecret).toString('base64')
const response = await fetch(authUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Accept-Language': 'en_US',
'Authorization': `Basic ${base64}`,
},
body: 'grant_type=client_credentials'
});
const data = await response.json();
return data;
}
setUpMerchant = async () => {
let authData = await this.getAuthToken();
const partnerUrl = "https://api-m.sandbox.paypal.com/v2/customer/partner-referrals";
let data = {
"operations": [
{
"operation": "API_INTEGRATION",
"api_integration_preference": {
"rest_api_integration": {
"integration_method": "PAYPAL",
"integration_type": "THIRD_PARTY",
"third_party_details": {
"features": [
"PAYMENT",
"REFUND"
]
}
}
}
}
],
"products": [
"EXPRESS_CHECKOUT"
],
"legal_consents": [
{
"type": "SHARE_DATA_CONSENT",
"granted": true
}
]
};
const request = await fetch(partnerUrl, {
method: 'POST',
headers: {
'Authorization': 'Bearer '+authData.access_token,
'Content-Type': 'application/json',
'data': data,
},
});
const partnerData = await request.json();
return partnerData;
}
Edit: I discovered the issue was I was running a GET request instead of a POST. The accepted answer is the correct URL
According to the documentation the URL to call is https://api-m.sandbox.paypal.com/v2/customer/partner-referrals
The production URL does not have sandbox. in the domain.
I've got some simple code that isn't working. Have tried several libraries, all of which wind up giving me similar errors. I'm convinced the website itself is somehow rejecting the request. It's wrapped in an electron application.
This is the response I receive (not a typical error - the error is in the response body)
{"error":{"code":2,"message":"Query missing. `Are ya gonna search for something or what?`"}}
This is a snippet of relevant code, present in the main.js of my app:
const request = require('request');
.
.
.
ipcMain.on('request-mainprocess-action', (e, args) => {
request({ body: JSON.stringify({"query": {"status": {"option": "online"}, "stats": [{"type": "and", "filters": []}]},"sort": {"price": "asc"}}}),
followAllRedirects: true,
headers: {
'Content-Type': 'application/json',
'Referer': 'www.pathofexile.com/trade/search/Incursion',
'X-Requested-With': 'XMLHttpRequest' },
method: 'POST',
url: 'http://pathofexile.com/api/trade/search/Incursion'}, callback);
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
e.sender.send('mainprocess-response', {
type: args.query,
data: body
});
} else {
console.log(body);
}
}
});
I've tried a few variations of this, like calling request.post or using a json object and the flag json:true. I've also tried some other libraries, like http, https, and axios(axiom?).... No luck.
I was able to make it work with little effort using an IntelliJ http post script/snippet:
POST https://www.pathofexile.com/api/trade/search/Incursion
Referer: https://www.pathofexile.com/trade/search/Incursion
Content-Type: application/json,
X-Requested-With: XMLHttpRequest
{"query": {"status": {"option": "online"}, "stats": [{"type": "and", "filters": []}]},"sort": {"price": "asc"}}
The above returns a rather large bit of text,
Response code: 200 (OK); Time: 1426ms; Content length: 6755 bytes
Unfortunately I cannot call this from Electron :)... Could really use your help.
Thanks,
Just add this line to your request-headers:
'Host': 'www.pathofexile.com',
And here is the complete code which I run it from my side with node command:
const request = require('request');
var payload = JSON.stringify({"query": {"status": {"option": "online"}, "stats": [{"type": "and", "filters": []}]},"sort": {"price": "asc"}});
request({
body: payload,
followAllRedirects: true,
headers: {
'Content-Type': 'application/json',
'Referer': 'www.pathofexile.com/trade/search/Incursion',
'Host': 'www.pathofexile.com',
'X-Requested-With': 'XMLHttpRequest' },
method: 'POST',
url: 'http://pathofexile.com/api/trade/search/Incursion'}, callback);
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log('Success: \n'+body);
} else {
console.log("Error: \n"+body);
}
};
And I got this result as shown on this screenshot:
I programmed a Node.JS Server with FCM and POST Protocol and my Client app is made with Swift code.
In Terminal, It prints success code like this
id=0:1476620858011270%00f7ba1cf9fd7ecd
But In my Client App, there is no notification. So I tried with the notification tab in my Firebase Console, and it worked very well.
This is my header in my server file
var headers = {
'Content-Type': 'application/json',
'Authorization': 'key=<Server Key>',
'project_id': '<Project ID>'
}
and This is my Request Code
request({
headers: headers,
url: "https://fcm.googleapis.com/fcm/send",
method: "POST",
form: {
"notification" :{
"title": "titie",
"body": "body"
},
"content-available": true,
"priority": "high",
"to": "<Firebase Token>"
}
}, function(error, response, body) {
console.log(body);
});
When sending several requests serially, it seems like
callbacks are executed when all requests are sent.
requests seems to get added into a queue but not actually getting executed before the loop is done.
Example
var http = require('http');
var i=0;
var postData = [];
while (i++ < 12) {
var largeObject = [
'this' + i,
'that' + i,
'then' + i
];
postData.push(largeObject);
if (postData.length >= 2) {
console.log('request');
var options = {
hostname: 'httpbin.org',
port: 80,
path: '/post',
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
};
var req = http.request(options, function(res) {
console.log(res.statusCode);
res.on('data', function(chunk) {
console.log(chunk.toString());
});
res.on('end', function() {
console.log('finished reading response');
});
});
req.end(JSON.stringify(postData), function() {
console.log('request stream finished');
});
postData = [];
}
}
The output here would look like
request
request
request
request
request
request
request stream finished
request stream finished
request stream finished
request stream finished
request stream finished
request stream finished
200
{
"args": {},
"data": "[[\"this7\",\"that7\",\"then7\"],[\"this8\",\"that8\",\"then8\"]]",
"files": {},
"form": {},
"headers": {
"Content-Length": "53",
"Content-Type": "application/json",
"Host": "httpbin.org"
},
"json": [
[
"this7",
"that7",
"then7"
],
[
"this8",
"that8",
"then8"
]
],
"origin": "xxxxx",
"url": "http://httpbin.org/post"
}
finished reading response
200
{
"args": {},
"data": "[[\"this1\",\"that1\",\"then1\"],[\"this2\",\"that2\",\"then2\"]]",
"files": {},
"form": {},
"headers": {
"Content-Length": "53",
"Content-Type": "application/json",
"Host": "httpbin.org"
},
"json": [
[
"this1",
"that1",
"then1"
],
[
"this2",
"that2",
"then2"
]
],
"origin": "xxxx",
"url": "http://httpbin.org/post"
}
...
Is there any way to finish one request before the next one is getting executed?
I don't really care about the order, it's more about memory - I'd like to get rid of large objects I am posting
e.g.
request1
response1
request2
response2
request3
response3
request4
request5
response5
response4
would be absolutely fine.
Any suggestion?
Of course, just use some control-flow modules like async or bluebird.
As you don't care about order, I'd advise you to use either async#parallel or bluebird#map. The bluebird#map has a concurrency argument, which can be nice when you need some more control over loops and their amount (so does async#parallelLimit).
If this doesn't seem straightforward, please comment and I'll add an example.
I was using bash to do a task. And had some mess while trying to parse the response. Now I am using nodejs for the task. But I get following error:
"httpStatus" : 415,
"messages" : [ {
"errorCode" : "305",
"message" : "Unsupported media type 'application/x-www-form-urlencoded'"
} ]
This used to be my curl request in bash file:
curl --include\
--request POST \
--user "$USERNAME:$PASSWORD" \
--header "Content-Type: application/vnd.profitbricks.resource+json" \
--data-binary "{
\"properties\": {
\"name\": \"$servername\",
\"ram\": $RAM,
\"cores\": $CORES
}
}" \
https://api.profitbricks.com/rest/datacenters/$ID/servers ;
This is my current request:
var request = require('request');
var reqoptions = {
method: 'POST',
uri: 'https://api.profitbricks.com/rest/datacenters/'+options.vdcID+'/servers',
form:{
"properties":{
"cores": options.cores,
"ram": options.ramsize,
"name": options.servername
}
},
headers: {
'Authorization': 'Basic ' + new Buffer(options.user+':'+options.password).toString('base64'),
'Content-Type': 'application/vnd.profitbricks.resource+json'
}
};
request(reqoptions, function(err, res, body){});
form option changing content-type to form-urlencoded
you shouldn't use form in request options
send a binary data like here nodejs/express and binary data in POST
so use body: myBuffer instead of form: {...}
The problem was serialization. I stringified the object. Now it works.
var request = require('request');
var body = {
"properties":{
"cores": options.cores,
"ram": options.ramsize,
"name": options.servername
}
}
var reqoptions = {
method: 'POST',
uri: 'https://api.profitbricks.com/rest/datacenters/'+options.vdcID+'/servers',
body: JSON.stringify(body),
headers: {
'Authorization': 'Basic ' + new Buffer(options.user+':'+options.password).toString('base64'),
'Content-Type': 'application/vnd.profitbricks.resource+json'
}
};
request(reqoptions, function(err, res, body){});
This did the trick.