Question

I'm in the process of rebuilding a PHP app in Node.js on top of the Express framework.

One part of the application is a callback url that an Amazon SNS notification is posted to.

The POST body from SNS is currently read in the following way in PHP (which works):

$notification = json_decode(file_get_contents('php://input'));

In Express I have tried the following:

app.post('/notification/url', function(req, res) {
    console.log(req.body);
});

However, watching the console, this only logs the following when the post is made:

{}

So, to repeat the question: How do you access an Amazon SNS post body with Express / Node.js

Was it helpful?

Solution

Another approach would be to fix the Content-Type header.

Here is middleware code to do this:

exports.overrideContentType = function(){
  return function(req, res, next) {
    if (req.headers['x-amz-sns-message-type']) {
        req.headers['content-type'] = 'application/json;charset=UTF-8';
    }
    next();
  };
}

This assumes there is a file called util.js located in the root project directory with:

util = require('./util');

in your app.js and invoked by including:

app.use(util.overrideContentType());

BEFORE

app.use(express.bodyParser());

in the app.js file. This allows bodyParser() to parse the body properly...

Less intrusive and you can then access req.body normally.

OTHER TIPS

This is based on AlexGad's answer.Particularly this comment:

The standard express parser will only handle application/json, application/x-www-form-encoded and multipart/form-data. I added some code above to place before your body parser.

app.post('/notification/url', function(req, res) {
    var bodyarr = []
    req.on('data', function(chunk){
      bodyarr.push(chunk);
    })  
    req.on('end', function(){
      console.log( bodyarr.join('') )
    })  
})

Take a look at AWS Node.js SDK - it can access all AWS service endpoints.

    var sns = new AWS.SNS();

    // subscribe
    sns.subscribe({topic: "topic", Protocol: "https"}, function (err, data) {
      if (err) {
        console.log(err); // an error occurred
      } else {
        console.log(data); // successful response - the body should be in the data
     }
   });


    // publish example
    sns.publish({topic: "topic", message: "my message"}, function (err, data) {
      if (err) {
        console.log(err); // an error occurred
      } else {
        console.log(data); // successful response - the body should be in the data
     }
   });

EDIT: The problem is that the standard body parser does not handle plain/text which is what SNS sends as the content type. Here is code to extract the raw body. Place it before your body parser:

app.use(function(req, res, next) {
    var d= '';
    req.setEncoding('utf8');
    req.on('d', function(chunk) { 
        d+= chunk;
    });
    req.on('end', function() {
        req.rawBody = d;
        next();
    });
});

You can then use:

JSON.stringify(req.rawBody));

within your route to create a javascript object and operate on the SNS post appropriately.

You could also modify the body parser to handle text/plain but its not a great idea to modify middleware. Just use the code above.

Here is how you can do that supposing that you're using body-parser.

Just add the following lines to your app.js:

app.use(bodyParser.json());
app.use(bodyParser.text({ type: 'text/plain' }));

This information can also be found in the body-parser official document:
https://github.com/expressjs/body-parser

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top