Core https library vs. npm 'request' library - node.js

I am running into a very strange issue when trying to use the built-in node https library.
Request Headers:
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
Authorization: bearerToken
},
};
Request body:
let body = JSON.stringify({
"content_type": "application/json",
"message" : message
});
This is my standard call using the default https library of node:
let req = https.request(requestDetails, function (res){
let responseBody = undefined;
res.on('body', function(res) {
responseBody = '';
});
res.on('data', function(chunk) {
responseBody += chunk;
});
res.on('end', function() {
console.log(responseBody);
});
});
req.write(body);
req.on('error', function(e) {
console.log(e);
});
req.end();
Now whenever I send this request to the relevant server I get a:
Your browser sent a request that this server could not understand.
Reference #7.24507368.1554749705.3185b29b
However when I use the popular 'request' library on NPM it works fine and I get the response I expect.
This leads be to believe there is something different in maybe the 'encoding' or 'chunking' of the requests between these two libraries, but I cannot figure out what.
Does anyone have experience with the Node https library and understand any gotcha's there?
I prefer to use built-in libraries as much as possible to keep my package size low.

When using native http or https modules, you need to use the querystring module to stringify your body.
const querystring = require('querystring');
let body = querystring.stringify({
"content_type": "application/json",
"message" : message
});
//also include the content length of your body as a header
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
'Content-Length' : body.length
Authorization: bearerToken
},
};
'request' is built on top of the native modules and does this internally when you pass it a json body

Related

Problem Interacting With Mojang Authentication Server

I am trying to get a client token from the Mojang Authentication API, which can be found here https://wiki.vg/Authentication. However, whenever I try to make a request, I get the following response:
{error: 'ForbiddenOperationException', errorMessage: 'Forbidden'}
The API indicates this is because my credentials are invalid but the errorMessage that I am getting does not match any of their examples. I tried doing the same request through python's Requests module, and it worked well, which leads me to believe I am not sending my https request properly. I am aware there is probably something very basic I am overlooking, but I would appreciate it if someone tells me what I am doing wrong.
Here is my code:
Python Code that works:
import requests
url = 'https://authserver.mojang.com/authenticate'
data = {"username":"--username--", "password":"--password--"}
res = requests.post(url, json=data)
print(res.json())
Javascript Code that Doesn't Work:
var https = require('https');
var options = {
host: 'authserver.mojang.com',
path: '/authenticate',
method: 'POST',
headers: {"username":"--username--","password":"--password--"}
}
https.request(options, (res)=>{
var body = ''
res.on('data', (d)=>{
body+=d;
});
res.on('end', ()=>{
resp = JSON.parse(body);
console.log(resp);
});
}).end();
The problem is that you're sending your credentials as HTTP headers directly instead of as POST data. Try this instead:
var https = require('https');
var data = JSON.stringify({"username":"--username--","password":"--password--"})
var options = {
host: 'authserver.mojang.com',
path: '/authenticate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
var req = https.request(options, (res)=>{
var body = ''
res.on('data', (d)=>{
body+=d;
});
res.on('end', ()=>{
resp = JSON.parse(body);
console.log(resp);
});
});
req.write(data);
req.end();

"Body" of a post request always in unicode

If I make a POST request to a specific server using a Chrome plugin, I can see the "response body" comes back just fine in a JSOn format.
However when I try to do this with either "request" or "https.request", the "body" comes back as unicode which I can't seem to decipher. Anyone know how I can the body to come back as regular JSON, or how I could decipher this unicode? I tried a few stackoverflow solutions to decipher the unicode but no luck.
raw body:
��RPP�M-.NLOUR�RP�I,�K�P�,V��/QHL.�,KU��J�O�*─
�⎽�����wt�
⎽U��┬�>H ---
If I do this: JSON.stringify(body, null, 4)
"\┤001°�\␉\┤0000\┤0000\┤0000\┤0000\┤0000\┤0000\┤0000��RPP�M-.NLOUR�RP�I,�K�P�,V��/QHL.�,KU�\┤0001�J�O�*─\°�⎽����\┤000°�┬├\┤000␊�\°⎽U�\┤0005\┤0000�┬�>H\┤0000\┤0000\┤0000"
And here's the two code snippets I use to try and make POST requests:
request({
'url': 'https://api.nike.com/launch/entries/v2',
'method': 'POST',
'json': entriesPayload,
'headers': {
'authorization': authId,
"Accept": "application/json, text/plain, */*",
'Content-Type': "application/json;charset=utf-8"
},
},
...
and:
var options = {
hostname: 'api.nike.com',
port: 443,
path: '/launch/entries/v2',
method: 'POST',
json: entriesPayload,
headers: {
'authorization': authId,
"Accept": "application/json, text/plain, */*",
'Content-Type': 'application/json;charset=utf-8'
}
};
var req = https.request(options, (res) => {
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(postData);
req.end();
Also I get some real weird stuff happening after I make the request, in my IDE's logs. This is what shows up - it like translates everything into some different symbols (the last 3 lines are how I restarted my nodejs server).
"⎻⎺⎼├": 443,
"␤⎺⎽├┼▒└␊": "▒⎻␋.┼␋┐␊.␌⎺└",
"␤▒⎽␤": ┼┤┌┌,
"⎽␊▒⎼␌␤": ┼┤┌┌,
"─┤␊⎼≤": ┼┤┌┌,
"⎻▒├␤┼▒└␊": "/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2",
"⎻▒├␤": "/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2",
"␤⎼␊°": "␤├├⎻⎽://▒⎻␋.┼␋┐␊.␌⎺└/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2"
£,
"└␊├␤⎺␍": "POST",
"␤␊▒␍␊⎼⎽": π
"▒┤├␤⎺⎼␋≥▒├␋⎺┼": "B␊▒⎼␊⎼ ␊≤J␤␉G␌␋O␋JSU≥I1N␋I⎽I└├⎻ZCI6I┘␌2YWI1NT␤┐LWM┬ZTM├NGV␤Y␋05MT┌┘LTJ┐Y┘A3Y┘F┘N2N␤MHN⎻Z≤J9.␊≤J0␌┼V≥␍CI6MTA┬LCJ⎻YXQ␋O┘E1NDM2N≥␌┬ODA⎽I└V4␌CI6MTU0M≥Y4MDY4MC┬␋▒XN≥I┘⎺␋␉2F1␍G±≤YWN┘I␋┬␋▒┼R⎻I┘⎺␋ZGV␋MTZ┘M2Q├YT±│OS00OTE3LWF␤MD┐├NWV␤N≥R└M┘U┬MWM4I␋┬␋␉GF0I┘⎺│NTQ≥N┘␌3MD±┬LCJ␤␍WQ␋O␋J┘␉20┤␉└┌⎼ZS5┐▒W␍⎻␍GF⎽I␋┬␋␌3V␋I┘⎺␋Y29├L└5⎻▒2U┤Y29├␉WV≤Y2U┤␌25⎼␌┼M┤␍2V␋I␋┬␋␌2J0I┘⎺␋␉└┌⎼ZT⎻␤␌HA␋LCJ≥Y3A␋O┌⎽␋Y29├␉WV≤Y2U␋XS┬␋␌HJ┤I┘⎺␋M└I≥M≥J┐Y└U├MWE1Y≤00ZWJ┐LT┐0Z└Y├MGU┬N≥V┘OGV┘NGQ│I␋┬␋␌HJ0I┘⎺␋␉└┌⎼ZT⎻┬␉HV≥I┼0.I┴▒┘└U2W≤␉__┬AD└J±⎻Z≥␉␋-VVUV┘H⎽⎼EI├┼T┌␍1I9°┬OSL▒┬┤8≥0Z3┐C␍␊G⎽␊O≤│≥␌␉3RB┌GKXV┤SDRDH±IYZ␉H1X⎼5␉Q-┼R≥└┬I␤│␉M0─I⎺R┤␋␍␤▒I␋├NC␍⎺G±6⎺XQ␋°R┴W5␍⎺SZ┼⎻4YR2TN6U␍9Q≥⎻61NS⎺⎻F␌2V13NJ└0P7│K5-09⎻▒1│6P␍M≤┼⎽IWF─␤II⎼G≤K┘HO⎻BV└┌┤␋A≥°┼AF┤K1GC┌ZGD⎺TC␋8⎻JY_⎺HI-E8D±M┐O4KSN⎻H97KLHO-┴Z│2┬YLJ2°␉0⎼F┐D≤≤└A␍K⎼9┴┬┤9┼│XF␍⎼≥⎽┘22≤39KD⎻-⎽⎻R┐I⎺MD2▒└␋┼L1CA│8─-␉L│DY└┬GQ02C├I0─┘±",
"A␌␌␊⎻├": "▒⎻⎻┌␋␌▒├␋⎺┼/┘⎽⎺┼, ├␊│├/⎻┌▒␋┼, */*",
"C⎺┼├␊┼├-T≤⎻␊": "▒⎻⎻┌␋␌▒├␋⎺┼/┘⎽⎺┼;␌␤▒⎼⎽␊├=┤├°-8",
"␌⎺┼├␊┼├-┌␊┼±├␤": 575
£
£
£
[┼⎺␍␊└⎺┼] ⎼␊⎽├▒⎼├␋┼± ␍┤␊ ├⎺ ␌␤▒┼±␊⎽...
[┼⎺␍␊└⎺┼] ⎽├▒⎼├␋┼± ◆┼⎺␍␊ ▒⎻⎻.┘⎽◆
E│⎻⎼␊⎽⎽ ⎽├▒⎼├␊␍ ⎺┼ ⎻⎺⎼├ 3000
The response body is not unicode encoded, but GZIP encoded (compressed data). We can check if a response is compressed with the Content-Encoding header:
var encoding = res.headers['content-encoding'];
https doesn't unzip the response body automatically like a browser does, and so you get all those strange characters instead of a JSON string. However, we can use the built-in zlib library and decdode the response to a string.
const https = require('https');
const zlib = require('zlib');
var options = {
hostname: 'api.nike.com',
port: 443,
path: '/launch/entries/v2',
method: 'POST',
headers: {
'Authorization': 'my-token',
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8'
}
};
var data = JSON.stringify({});
var req = https.request(options, (res) => {
var encoding = res.headers['content-encoding'];
console.log('code:', res.statusCode);
console.log('encoding:', encoding);
deflate = zlib.createGunzip();
res.pipe(deflate);
deflate.on('data', (chunk) => {
console.log('data: ' + chunk.toString());
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(data);
req.end();
The chrome plugin that you're using to POST is probably setting the content-type correctly, while your regular request from your server isn't.
Can you change your request so it includes
request({
'url': externalUrl,
'method': 'POST',
'json': entriesPayload,
'Content-Type': 'application/x-www-form-urlencoded'
});

nodejs requestify.request() crashes when the method is given in the options

Why does requestify.request() crash because it doesn't think it was given the method POST
var postBody = querystring.stringify(dat);
var postOptions = {
host: 'https://www.example.com'
, path: '/admin'
, method: 'post' // POST, 'POST', post
, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postBody) }
};
var postReq = requestify.request(postOptions, function (err, resp, respBody) {
resp.setEncoding('utf8');
resp.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
It looks like you are using an old version of the documentation for the newer version of requestify
Read the API reference section of https://github.com/ranm8/requestify
And read just a little lower down it see a requestify.request() example.
Options doesn't have host, path, ...

Node HTTPS Request – MalformedJsonException

I'm trying to make a POST request using node/HTTPS, but I keep getting this error:
BODY: {"message":"MalformedJsonException:
com.google.gson.stream.MalformedJsonException: Use
JsonReader.setLenient(true) to accept malformed JSON at line 1 column
11 path $","mdcKey":""}
Here's the code that initiates the request:
const https = require('https');
const querystring = require('querystring');
const POSTData = querystring.stringify({
'addressTo': 'myemail#address.com',
'subject': 'testing your email template',
'templateName': 'genericTemplate',
'bodyText': 'this is a test'
});
console.log(POSTData);
const HTTPOptions = {
hostname: 'url.com',
port: 00000,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const HTTPSrequest = https.request(HTTPOptions, (response) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
response.setEncoding('utf8');
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
response.on('end', () => {
console.log('No more data in response.');
});
});
HTTPSrequest.on('error', (error) => {
console.log(`problem with request: ${error}`);
});
// write data to request body
HTTPSrequest.write(POSTData);
HTTPSrequest.end();
I'm assuming it's the POSTData that is the "malformed JSON", here's what it looks like stringified:
addressTo=name%40companyname.com&subject=testing%20your%20email%20template&templateName=genericTemplate&bodyText=this%20is%20a%20test
I'm not sure what I'm doing that is causing the malformed JSON. What am I missing?
You're sending an application/x-www-form-urlencoded payload but telling the server it's application/json. The server is complaining because of this mismatch.
The simple solution should be to just change querystring.stringify to JSON.stringify.
You may also need to specify a Content-Length header since some servers may not support chunked requests (which is the default in node). If you do add this, make sure you use Buffer.byteLength(POSTData) as the header value instead of just POSTData.length since the former works for multi-byte characters and the latter returns the number of characters (not bytes).
Help more with explication from #mscdex
Your options object after querystring.stringify has returned. Otherwise you won't know the length of the stringified body data.
It is important to note that you need two extra headers in your options object in order to make a successful post request. These are :
'Content-Type': 'application/json',
'Content-Length': POSTData.length
Check example:
const HTTPOptions = {
hostname: 'url.com',
port: 00000,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': POSTData.length
}
};
You can see this examples too: 1st post.
JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $","mdcKey":""}

Mailchimp API v3.0 add email to list via NodeJS http

I'm using NodeJS to call the new MailChimp 3.0 API in order to add an email to a list. While I can get it working via POSTman, I'm having a hard time with Node's http:
var http = require('http');
var subscriber = JSON.stringify({
"email_address": "test#test.com",
"status": "subscribed",
"merge_fields": {
"FNAME": "Tester",
"LNAME": "Testerson"
}
});
var options = {
host: 'https://us11.api.mailchimp.com',
path: '/3.0/lists/<myListID>/members',
method: 'POST',
headers: {
'Authorization': 'randomUser myApiKey',
'Content-Type': 'application/json',
'Content-Length': subscriber.length
}
}
var hreq = http.request(options, function (hres) {
console.log('STATUS CODE: ' + hres.statusCode);
console.log('HEADERS: ' + JSON.stringify(hres.headers));
hres.setEncoding('utf8');
hres.on('data', function (chunk) {
console.log('\n\n===========CHUNK===============')
console.log(chunk);
res.send(chunk);
});
hres.on('end', function(res) {
console.log('\n\n=========RESPONSE END===============');
});
hres.on('error', function (e) {
console.log('ERROR: ' + e.message);
});
});
hreq.write(subscriber);
hreq.end();
Rather than getting even some sort of JSON error from Mailchimp, however, I'm getting HTML:
400 Bad Request
400 Bad Request
nginx
Is it clear at all what I"m doing wrong here? It seems pretty simple, yet nothing I've tried seems to work.
A few additional thoughts:
While http's options have an "auth" property, I'm using the headers instead to ensure the authorization is sent without the encoding (as mentioned here). Still, I've also tried with the "auth" property, and I get the same result.
I'm actually making this call from inside an ExpressJS API (my client calls the Express API, that calls the above code - I've edited all that out of this example for simplicity). That's why my variables are "hres" and "hreq", to distinguish them from the "res" and "req" in Express. Is there any reason that could be the issue?
As mentioned above, I am able to get successful results when using POSTman, so I at least know my host, path, list ID, and API key are correct.
It turns out this had a very simple solution: the "host" property of the options object needed to have only the domain name. IE, remove the "https://" protocol:
var options = {
host: 'us11.api.mailchimp.com',
path: '/3.0/lists/<myListID>/members',
method: 'POST',
headers: {
'Authorization': 'randomUser myApiKey',
'Content-Type': 'application/json',
'Content-Length': subscriber.length
}
}
Try this , its working fine for Me.
var request = require('request');
function mailchimpAddListCall(email, cb){
var subscriber = JSON.stringify({
"email_address": email,
"status": "subscribed"
});
request({
method: 'POST',
url: 'https://us13.api.mailchimp.com/3.0/lists/<Your list id>/members',
body: subscriber,
headers:
{
Authorization: 'apikey <your Mailchimp API key>',
'Content-Type': 'application/json'
}
},
function(error, response, body){
if(error) {
cb(err, null)
} else {
var bodyObj = JSON.parse(body);
console.log(bodyObj.status);
if(bodyObj.status === 400){
cb(bodyObj.detail, null);
}
var bodyObj = JSON.parse(body);
cb(null, bodyObj.email_address +" added to list.");
}
});
}
request is a node module, that you'll need to install into your package.json. npm install --save request
You can use the auth properties just fine with API v3, but if you're getting a 400, that's not the problem. The body of the 400 Error should provide more detailed information, but one thing that jumps out immediately: MailChimp doesn't allow fake or fake-looking emails to be added to lists (like test#test.com), so I'd try a real address and see if that works for you.

Resources