POST from Slack for button interactions has empty body - node.js

I'm setting up a Slack bot using node.js. The event system works perfectly and gives me POSTs with valid bodies from Slack, and I am able to successfully send messages (both interactive and not) to Slack.
However, the POSTs Slack sends me in response to an interaction with the buttons on interactive messages has an empty body. Interestingly, the Slack headers are still well-formed, although it fails to pass the signing secret test (which I know I implemented properly since event POSTs from Slack pass it).
I've set up everything for interactions according to Slack's own documentation here: https://api.slack.com/messaging/interactivity/enabling. I'm using express, request, and XMLHttpRequest to receive and send HTTP methods. If anyone has encountered this problem or has any insights, that would be great. Thanks!
Here's a code snippet for my function receiving POSTs from interactions:
var express = require('express');
var request = require('request');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
app.post('/interaction', (req, res) => {
res.sendStatus(200);
var payload = {
"channel": req.body.payload.channel, // Breaks here since req.body is empty
"text": "Selected choice " + req.body.payload.actions.text.text
}
var r = new XMLHttpRequest();
r.onload = () => { var status = request.status; var data = request.responseText; }
r.open("POST", request_url, true);
r.setRequestHeader("Content-Type", "application/json");
r.setRequestHeader("Authorization", "Bearer " + botToken);
r.send(JSON.stringify(payload));
});

The Slack documentation doesn't seem to mention this, but empirically it would seem that the content type for webhook calls to apps uses Content-Type: application/x-www-form-urlencoded. You'll want to add:
app.use(bodyParser.urlencoded({ extended: true }));
In addition, the payload parameter can't be accessed as you're doing: it's actually a JSON object serialized as a string. This is documented here: https://api.slack.com/messaging/interactivity/enabling#understanding_payloads
The request will be made to your specified request URL in an HTTP POST. The body of that request will contain a payload parameter. Your app should parse this payload parameter as JSON.
So your code will want to do something like this:
var slack_payload = JSON.parse(req.body.payload);
var payload = {
"channel": slack_payload.channel,
"text": "Selected choice " + slack_payload.actions.text.text
}

Related

Where is POST method saved in express?

So I'm new to Backend and is learning about HTTP request. As far as I know GET is for getting a resource from backend and displays it on the browser. Then there is POST to post a resource, but where does it posted? I'm using express as framework. This is my code:
let bodyParser = require('body-parser');
let express = require('express');
let app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.post('/name', (req, res) => {
let first = req.body.first;
let last = req.body.last;
res.json({
'name': `${first} ${last}`
})
})
module.exports = app;
I successfully displayed the respond (a JSON with properties name: ${first} ${last}) after I submitted the form. But where does this JSON is saved? How can I access the JSON again?
Do I still need to push it to database to have access to submitted JSON? Because I thought before, POST will POST the submitted value.
Any helpful answer would be appreciated.
res.json is similar to sending response data back to the client side. At the client side, you can use a variable to store the result of calling this http request and then you can use the data it sends back.

ajax call cant pass parameter to nodejs app

I make a ajax call via code below and expect to see parameters passed nodejs app, but it always empty
#action postData() {
$.ajax({
url:"http://localhost:8080/sendMail",
ContentType:'application/json',
dataType:'json',
type:'post',
data:JSON.stringify({'message':'helloworld'}),
success:(result) =>{
..
})
If I make a post request within postman(adding raw string json parameters to body; {'message':'helloworld'}) its passed well and I see its logged. So whats wrong with this ajax call that i used in reactjsapp ?
Edited: it looks all parameters passed in browser fine but somehow nodejs unable to get them..
Since POST data is sent in the HTTP body, you need to parse the JSON in there to get it. Assumed you use express.js on the server side, you could use body-parser to do that:
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())

Setting the Response Header Using Express Node

I am using the following code to set the response header:
var express = require("express");
var app = express();
app.get("/",function(req,res){
res.header('Content-Type','application/json');
var task = { title :"Do the grocery" }
res.send(JSON.stringify(task));
});
When I see in my response in Google Chrome I don't see the response header "Content-Type" being set. Am I doing something wrong?
That's not a method afaik, should be:
res.set('Content-Type', 'application/json');
As a sidenote, if you call res.json() you can just pass it an object and it'll json stringify it for you.

Mailgun webhook POST body seems empty

I'am trying to handle http post message from Mailgun bounce webhook. When sending it to Mailgun's Postbin service all data is found of course. But I'm now sending that POST to my localhost server for development purposes and all I get is empty json array. I use Test Webhook.
Intent is to keep this simple as possible besides our main service. That for I started using nodejs/expressjs to create stand alone webservice to work as relay to receive POST messages of email bounces from Mailgun and inform admins about bounced email addresses.
Now I can't figure why I don't get the same data as is visible in Postbin.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mailgun = require('mailgun-js')({apiKey: 'key-...', domain: 'mymailgundomain.com'});
app.use(bodyParser.urlencoded({
extended: true
}));
function router(app) {
app.post('/webhooks/*', function (req, res, next) {
var body = req.body;
if (!mailgun.validateWebhook(body.timestamp, body.token, body.signature)) {
console.error('Request came, but not from Mailgun');
res.send({ error: { message: 'Invalid signature. Are you even Mailgun?' } });
return;
}
next();
});
app.post('/webhooks/mailgun/', function (req, res) {
// actually handle request here
console.log("got post message");
res.send("ok 200");
});
}
app.listen(5000, function(){
router(app);
console.log("listening post in port 5000");
});
I'm running this from Mailgun's Test Webhook using url like http://mylocalhostwithpublicip.com:5000/webhooks/mailgun
Code structure is copied from https://github.com/1lobby/mailgun-js. Probably I'm missing something fundamental here as I can't figure it out myself.
The reason you're not seeing req.body populated is because the body-parser module does not support multipart/form-data requests. For those kinds of requests you need a different module such as multer, busboy/connect-busboy, multiparty, or formidable.
If your content-type (shown by logging console.dir(req.headers['content-type'])) is 'application/x-www-form-urlencoded', and you're using body-parser, try adding the following line:
bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
to make it work with multer, you can use .any() (version 1.1.0)
for me it worked like this: (assuming multer is included and declared as "multer")
post('/track', multer.any(),function(req, res){
//if body is a string, parse the json
var data=(typeof req.body=='string')?JSON.parse(req.body):req.body;
//if data is an object but you can't verify if a field exists with hasOwnProperty, force conversion with JSON
if(typeof data=='object' && typeof data.hasOwnProperty=='undefined')
data=JSON.parse(JSON.stringify(data));
//data is your object
});
var multer = require('multer');
var msg = multer();
post('/track', msg.any(), function(req, res){
console.log(req.body);
}
I make a custom parser for get data in req.body when the Content-type = 'multipart/alternative'
https://github.com/josemadev/Multiparser/

express req.pipe() does not work

I want to listen to incoming POST request in express.
I want to pipe this request to another server
I want to receive response inside express handler (I dont want to pipe response to express res stream)
For now I have following code:
app.post('server1',function(req,res,next){
var request = require('request');
req.pipe(request.post('server2')).pipe(res);
}
So this does not work - request is not even piped to server2 - I checked it and there is no incoming request.
I solved points 1 & 2 like this:
var bodyParser = express.bodyParser();
app.use(function(req,res,next){
if(req.path == '/server1' && req.method == 'POST') {
return next();
}
else {
bodyParser(req,res,next);
}
});
Not very nice but it works - it just disables bodyparser for a single route (POST /server1).
But I still don't know how to obtain json response body from piped request - I have following code:
app.post('/server1',function(req,res,next){
var request = require('request');
var pipe = req.pipe(request.post('/server2'));
pipe.on('end',function(){
var res2 = pipe.response;
console.log(res2);
});
});
res2 object has correct statusCode and headers and so on but it does not contain body - how I can get this from the res2 object? /server2 returns some data in json but I dont know how to read it from response...
It doesn't work because bodyParser intercepts all the bodies with parsers
I think you're almost there. You should listen on data events on the pipe to collect the response:
app.post('/server1',function(req,res,next) {
var request = require('request');
var pipe = req.pipe(request.post('/server2'));
var response = [];
pipe.on('data',function(chunk) {
response.push(chunk);
});
pipe.on('end',function() {
var res2 = Buffer.concat(response);
console.log(res2);
// don't forget to end the 'res' response after this!
...
});
});
However, since you solved the "bodyParser() getting in the way" problem, you can also use your initial pipe setup if you just want to return the response generated by server2 (also, be sure to use proper URL's when using request).

Resources