how to send twilio sms using node https request - node.js

I'm trying to send an sms without the twilio sdk by using the nodejs https module however the twilio post api keeps responding with this error "400, Bad Request", which means I'm probably not crafting the request the right way. I've followed the nodejs docs https example, and also twilio's. I've also tried making curl post request and it works perfectly fine. Where I'm I getting it wrong. Here's my code
// Send an SMS message via Twilio
helpers.sendTwilioSms = (phone, message, callback) => {
// validate parameters
phone =
typeof phone == "string" && phone.trim().length == 10
? phone.trim().length
: false;
message =
typeof message == "string" &&
message.trim().length > 0 &&
message.trim().length <= 1600
? message.trim()
: false;
if (phone && message) {
// Configure the request payload
const payload = {
from: config.twilio.fromPhone,
to: `+234${phone}`,
body: message
};
// stringify payload using querystring module instead of JSON.stringify because the reqeust we'll be sending is not of application/json but 'application/x-www-form-urlencoded' form content-type as specified by Twilio
const stringPayload = querystring.stringify(payload);
// Configure the request details
var requestDetails = {
hostname: "api.twilio.com",
method: "POST",
path: `/2010-04-01/Accounts/${config.twilio.accountSid}/Messages.json`,
auth: `${config.twilio.accountSid}:${config.twilio.authToken}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(stringPayload)
}
};
// Instantiate the request
const req = https.request(requestDetails, res => {
// grab the status of the sent request
const status = res.statusCode;
console.log([
`(sendTwilioSms) making https post request`,
`(sendTwilioSms) response completed: ${res.complete}`,
`(sendTwilioSms) response statusCode: ${res.statusCode}`,
{ "(sendTwilioSms) response headers:": res.headers },
{ "(sendTwilioSms) response body:": res.body }
]);
// callback successfully if the request went through
if (status == 200 || status == 201) {
callback(false);
} else {
callback(500, {
Error: `Status code returned was ${status}: ${res.statusMessage}`
});
}
});
// Alert the user as to a change in their check status
workers.alertUserToStatusChange = newCheckData => {
const message = `Alert: Your check for ${newCheckData.method.toUpperCase()} ${
newCheckData.protocol
}://${newCheckData.url} is currently ${newCheckData.state}`;
helpers.sendTwilioSms(newCheckData.userPhone, message, err => {
if (!err) {
console.log(
"Success: User was aterted to a status change in their check, via sms: ",
msg
);
} else {
console.log(
"Error: Could not send sms alert to user who add a state change in their check"
);
}
});
Here's the Response:
[
'(workers) making check request',
'(workers) check response completed: false',
'(workers) check response statusCode: 200'
]
logging to file succeeded
Check outcome has not changed no alert needed
[
'(sendTwilioSms) making https post request',
'(sendTwilioSms) response completed: false',
'(sendTwilioSms) response statusCode: 400',
{
'(sendTwilioSms) response headers:': {
date: 'Fri, 17 Jan 2020 09:49:39 GMT',
'content-type': 'application/json',
'content-length': '127',
connection: 'close',
'twilio-request-id': 'RQ7ee0b52d100c4ac997222f235e760fb7',
'twilio-request-duration': '0.025',
'access-control-allow-origin': '*',
'access-control-allow-headers': 'Accept, Authorization, Content-Type, If-Match, '
+
'If-Modified-Since, If-None-Match, ' +
'If-Unmodified-Since',
'access-control-allow-methods': 'GET, POST, DELETE, OPTIONS',
'access-control-expose-headers': 'ETag',
'access-control-allow-credentials': 'true',
'x-powered-by': 'AT-5000',
'x-shenanigans': 'none',
'x-home-region': 'us1',
'x-api-domain': 'api.twilio.com',
'strict-transport-security': 'max-age=31536000'
}
},
{ '(sendTwilioSms) response body:': undefined }
]
Error: Could not send sms alert to user who add a state change in their check

Try with something like this:
// authentication
var authenticationHeader = "Basic "
+ new Buffer(config.twilio.accountSid
+ ":"
+ config.twilio.authToken).toString("base64");
// Configure the request details
var requestDetails = {
host: "api.twilio.com",
port: 443,
method: "POST",
path: `/2010-04-01/Accounts/${config.twilio.accountSid}/Messages.json`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(stringPayload),
"Authorization": authenticationHeader
}
};
and this:
// Instantiate the request
const req = https.request(requestDetails, res => {
// grab the status of the sent request
const status = res.statusCode;
res.setEncoding('utf8');
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log('Successfully processed HTTPS response');
// If we know it's JSON, parse it
if (res.headers['content-type'] === 'application/json') {
body = JSON.parse(body);
}
callback(null, body);
});
// callback successfully if the request went through
if (status == 200 || status == 201) {
callback(false);
} else {
callback(500, {
Error: `Status code returned was ${status}: ${res.statusMessage}`
});
}
});
I hope it works, I have not tested. If it doesn't let me know and I'll try on my side and post a complete tested code.

Related

Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response

I'm getting this error after the preflight request:
Access to fetch at 'myInvokeUrl' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
My goal is to fetch a POST request to API Gateway. I added the Access-Control-Allow-Headers header to my OPTIONS response in the Gateway console with a value of 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', but the actual response doesn't have this field at all.
Response Headers:
access-control-allow-methods: POST,OPTIONS
access-control-allow-origin: *
content-length: 36
content-type: application/json
date: Mon, 28 Oct 2019 21:13:22 GMT
status: 200
My Lambda function (it's working if I test it in API Gateway):
module.exports.sendM = function (event, context, callback) {
console.log(event);
var body =`<p>Olá, ${event.name}!</p><p>Esse é o resumo do seu pedido: </p>`;
let minion1 = event.minion;
let minion2 = event.minion2;
let minion3 = event.minion3;
if (event.minion1){
body += `<p> Au Naturel: ${event.minion1}</p>`;
}
if (event.minion2){
body += `<p> Phil: ${event.minion2}</p>`;
}
if (event.minion3){
body += `<p> Bored Silly Kevin: ${event.minion3}</p>`;
}
body += `<p> Enviar para ${event.address}</p>`;
var mailOptions = {
from: 'leosole#gmail.com',
subject: 'Pedido minionshop',
html: body,
to: `${event.mail}`
// bcc: Any BCC address you want here in an array,
};
// create Nodemailer SES transporter
var transporter = nodemailer.createTransport({
SES: ses
});
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": true, // Required for cookies, authorization headers with HTTPS
"Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
"Access-Control-Allow-Methods": "POST, OPTIONS"
},
body: JSON.stringify(event)
};
// send email
console.log(event.mail);
transporter.sendMail(mailOptions, function (err, info) {
if (err) {
console.log("Error sending email");
callback(err);
} else {
console.log("Email sent successfully");
callback(null, response);
}
});};
The request:
handleSubmit = event => {
event.preventDefault();
console.log('trying to fetch')
fetch('myInvokeUrl', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
// 'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
name: this.state.name,
mail: this.state.mail,
address: this.state.address,
minion1: this.state.minion1,
minion2: this.state.minion2,
minion3: this.state.minion3
})
})
.then(function(response) {
return response.json()
}).then(function(json) {
console.log('parsed json', json)
}).catch(function(ex) {
console.log('parsing failed', ex)
})}
My integration response
Navigate to your Method Response post method (not options) and check if you have these configurations:
Then navigate to your Integration Response post method (not options) and check if you have these configurations:
Try it first without the send email function, just callback(null, { response }); before send email;
Why?
send email could cause Internal server error (500) meaning you will have to add another HTTP Status to your both POST,OPTIONS Integration Response methods.
Hope that help

400 Bad request while fetching data in post call

I am running my React js web app in one port 3000.
For node server I am using 4000.
While calling fetch method it returns `400 Bad request'.
Error
POST http://localhost:4006/auth/admin 400 (Bad Request)
react code npm started in 3000 port
fetch('http://localhost:4000/auth/admin',
{ mode: 'no-cors',
method: "POST",
body: JSON.stringify({
username:"admin",
password:"1234"
}),
headers: {
"Content-Type": "application/json",
'Accept': 'application/json, text/plain, */*',
credentials: "omit", //
// "Content-Type": "application/x-www-form-urlencoded",
},
})
.then((response) => console.log(response));
node code running in 4000 port
const passport = require("passport");
const route = require("../constants/routeStrings");
const keys = require("../config/keys");
const processStatus = require("../constants/processStatus");
const success = {
status: processStatus.SUCCESS
};
const failute = {
status: processStatus.FAILURE
};
module.exports = app => {
app.post('/auth/admin', passport.authenticate("local"), (req, res) => {
res.send(success);
});
};
Do not stringify the body. Change from
body: JSON.stringify({
username:"admin",
password:"1234"
}),
to
body: {
username:"admin",
password:"1234"
},
The 400 response is raised by passport since it is unable to read your params. You need to tell your "node" app to parse them before your actual routes.
// Import body parser, you should read about this on their git to understand it fully
const parser = require('body-parser');
const urlencodedParser = parser.urlencoded({extended : false});
// before your routes
app.use(parser .json());
app.use(urlencodedParser) // This will parse your body and make it available for your routes to use
Then do your other calls.
Also, make sure that you are sending username and password keys, otherwise read the documentation on how to change these key names to something else
I suffered long hours, but I overcame it throw writing those lines of code blocks. I successfully send the request to the server's controller, hopefully yours: make it try.
First define a async function to make POST request:
async function _postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
redirect: 'follow',
referrerPolicy: 'no-referrer',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: JSON.stringify(data)
});
return response.json();
}
Now create a request JSON payload:
let requestPayload = {
propertyName1: 'property value1',
propertyName2: 'property value23',
propertyName3: 'property value',
So on
}
Note: Request model will be your desired model, what request payload you actually send.
Now make a request using this payload including your end point URL:
_postData('http://servername/example', requestPayload )
.then(json => {
console.log(json) // Handle success
})
.catch(err => {
console.log(err) // Handle errors
});
100% worked on my project.

gibberish instead of html response as body in node.request

I'm making a HTTP request to a password protected site using the request module in npm, putting in a password, storing a cookie, then making the request once the cookie is stored and verified. I am able to get the same headers as I would on a normal browser request, but hte body itself, instead of being the HTML document I get in a browser, just looks like this:
� �Z�r�H��c��䞙��pT���Ī$3�ƾ�~Y�#�MK8���>*��?�z)�?U���ݨ�J�혳��섯tB��x��c��?�����������0�H�����V��O'�7����}���L�"˖}/ta�xn�g#�ݱ�O�����
Any ideas what might be causing this or how I can fix it?
In addition, when I run this from the command prompt, the computer "dings"
Here is the full node.js code I am running (minus the URLs/passwords/etc.)
var parsedurl1 = url.parse( urlstring1 );
var options1 = {
hostname: parsedurl1.hostname,
port: ( parsedurl1.port || 80 ), // 80 by default
method: 'POST',
path: parsedurl1.path,
headers: {
'Host': hostname
,'User-Agent': myuseragent
,'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
,'Accept-Language':"en-US,en;q=0.5"
,'Accept-Encoding': "gzip, deflate"
,'Referer': hostname
,'Content-Type': "text/html; charset=utf-8"
,'Content-Length': Buffer.byteLength(postData)
,'Connection': "keep-alive"
,'Upgrade-Insecure-Requests': "1"},
};
var cookiefile ;
var postData=querystring.stringify({
'email': myemail
,'password':mypassword
,'action': 'login'
,'go.x': 0
,'go.y': 0
})
var cookiefile;
var callback = function ( response ) {
// display returned cookies in header
var setcookie = response.headers["set-cookie"];
if ( setcookie ) {
setcookie.forEach(
function ( cookiestr ) {
cookiefile = cookiestr;
fs.writeFile(cookiefilelocation, cookiestr);
console.log( "COOKIE:" + cookiestr );
}
);
}
var data = "";
response.on(
"data",
function ( chunk ) { data += chunk; }
);
response.on(
"end",
function () {
newcookiefile = cookiefile.substr(0, cookiefile.indexOf(";"));
var parsedurl2 = url.parse( urlstring2 );
var options2 = {
url: urlstring2,
// port: ( parsedurl2.port || 80 ), // 80 by default
method: 'GET',
// path: parsedurl2.path,
headers: {
"Host": hostname
,"User-Agent": myuseragent
,'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
,'Accept-Language':"en-US,en;q=0.5"
,'Accept-Encoding': "gzip, deflate"
,'Referer':hostname
,'Cookie': newcookiefile
,'Connection': "keep-alive"
,'Upgrade-Insecure-Requests': "1"},
};
function callback3(error, response, body){
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response.headers ); // Print the response status code if a response was received
console.log('body:', body.substr(1,1000));
fs.writeFile('./config/pullfiles/mostrecent.txt', body);
}
requestlib(options2, callback3);
}
);
};
var request = http.request(options1, callback);
request.on(
"error",
function( err ) {
console.error( "RERROR:" + err );
}
);
request.write(postData);
request.end(); // let request know it is finished sending
I figured it out! This gibberish was caused by my lack of the 'gzip: true' option is my request(). Now the second request reads:
url: urlstring2
,gzip: true
,headers:{...

nodejs set encoding of body using request module

I have to send a post request with a body containing russian characters to a service which seems to only accept win1251 encoding. I am doing this in node.js using the request module.
This is how I am sending the request
var rawbody = "i1=&i2=&i3=" + query + "&i4=&i5=&i8=";
var options = {
url: this.initiate_endpoint,
jar: this.session,
cert: this.fs.readFileSync('resources/r.crt.pem', 'utf-8'),
key: this.fs.readFileSync('resources/r.key.pem', 'utf-8'),
timeout: 20000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: rawbody
};
let cookie = this.session;
request.post(options,
function (error, response, body) {
if( error ) {
callback(cookie, error);
}
else if( response.statusCode !== 200 ) {
callback(cookie, 'Responded with status code: ' + response.statusCode);
}
else {
callback(cookie, false);
}
});
Whenever the query variable is containing russian letters the request will fail.
For example:
var query = 'Путин';
So my questions are how to actually convert the encoding from the query variable to win1251 and furthermore how to set the encoding of the actual raw postbody, because I think those two things could be the cause of the issue.

Podio API addItem call

I'm trying to implement https://developers.podio.com/doc/items/add-new-item-22362 Podio API addItem call in a nodejs module. Here is the code:
var _makeRequest = function(type, url, params, cb) {
var headers = {};
if(_isAuthenticated) {
headers.Authorization = 'OAuth2 ' + _access_token ;
}
console.log(url,params);
_request({method: type, url: url, json: true, form: params, headers: headers},function (error, response, body) {
if(!error && response.statusCode == 200) {
cb.call(this,body);
} else {
console.log('Error occured while launching a request to Podio: ' + error + '; body: ' + JSON.stringify (body));
}
});
}
exports.addItem = function(app_id, field_values, cb) {
_makeRequest('POST', _baseUrl + "/item/app/" + app_id + '/',{fields: {'title': 'fgdsfgdsf'}},function(response) {
cb.call(this,response);
});
It returns the following error:
{"error_propagate":false,"error_parameters":{},"error_detail":null,"error_description":"No matching operation could be found. No body was given.","error":"not_found"}
Only "title" attribute is required in the app - I checked that in Podio GUI. I also tried to remove trailing slash from the url where I post to, then a similar error occurs, but with the URL not found message in the error description.
I'm going to setup a proxy to catch a raw request, but maybe someone just sees the error in the code?
Any help is appreciated.
Nevermind on this, I found a solution. The thing is that addItem call was my first "real"-API method implementation with JSON parameters in the body. The former calls were authentication and getApp which is GET and doesn't have any parameters.
The problem is that Podio supports POST key-value pairs for authentication, but doesn't support this for all the calls, and I was trying to utilize single _makeRequest() method for all the calls, both auth and real-API ones.
Looks like I need to implement one for auth and one for all API calls.
Anyway, if someone needs a working proof of concept for addItem call on node, here it is (assuming you've got an auth token beforehand)
_request({method: 'POST', url: "https://api.podio.com/item/app/" + app_id + '/', headers: headers, body: JSON.stringify({fields: {'title': 'gdfgdsfgds'}})},function(error, response, body) {
console.log(body);
});
You should set content-type to application/json
send the body as stringfied json.
const getHeaders = async () => {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
};
const token = "YOUR APP TOKEN HERE";
headers.Authorization = `Bearer ${token}`;
return headers;
}
const createItem = async (data) => {
const uri = `https://api.podio.com/item/app/${APP_ID}/`;
const payload = {
fields: {
[data.FIELD_ID]: [data.FIELD_VALUE],
},
};
const response = await fetch(uri, {
method: 'POST',
headers: await getHeaders(),
body: JSON.stringify(payload),
});
const newItem = await response.json();
return newItem;
}

Resources