In lambda, Could not parse request body into json - node.js

I try to make backend with lambda.
So I make the sample of it and post the data by postman.
I select form-data and put data in key and value.
and it returns like that.
{"message": "Could not parse request body into json: Unrecognized token \'Idx\': was expecting \'null\', \'true\', \'false\' or NaN\n at [Source: [B#745d4999; line: 1, column: 5]"}
So I find the some of docs, if I use postman to test lambda, select row and post the data like
{
"key" : "params"
}
But I want to receive the data in lambda when I post the data by form-data type.
It is simple test of it
In lambda,
exports.handler = (event, context, callback) => {
// TODO implement
const Idx = event.Idx * 2;
callback(null, Idx);
};
and, when I post the data json type
and when I post the data form-data type

When using form data your event.body is not a JSON, it’s a string that you need to parse. Specifically, it’s a query string. So in your case, it is:Idx=2
What you need to do is parse it to a JSON and then operate on it.
You can use a module for that
const querystring = require('querystring');
And inside the lambda
const body = querystring.parse(event.body)
Now you can access the Idx using body[‘Idx’]
You can use typeof to differentiate if it is an object or a string like so:
if (typeof(event.body) === ‘object’)
Edit: full code
const querystring = require('querystring');
exports.handler = (event, context, callback) => {
// TODO implement
console.log(event.body);
var Idx = null;
if (typeof(event.body) === ‘object’) {
Idx = event.Idx * 2;
} else if (typeof(event.body) === ‘string’) {
Idx = querystring.parse(event.body)[‘Idx’];
}
callback(null, Idx);
};

Related

JSON as Javascript object in Azure http trigger function

const df = require("durable-functions");
module.exports = async function (context, req) {
const client = df.getClient(context);
context.log(`Function Name = '${req.params.functionName}'.`);
context.log(`Body = '${req.body}'.`);
const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
context.log(`Started orchestration with ID = '${instanceId}'.`);
return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};
I have tried to use POSTMAN or https://reqbin.com/ for testing but I always get object.
It is a simple case but I don't understand why it is not JSON object.
I read this one
TypeScript Azure Function Read Body of POST method as JSON
but it didn't help me.
Everything is fine!
It is already deserialized json object.
It looks like the normal string in a log after
JSON.stringify(req.body)
My fault was that I wrongly got it in the orchestrator function from the context.

Unable to parse JSON response from SQS in Lambda node.js function

I am trying to get some data via Webhook into lambda function using API Gateway and SQS. This Webhook contains a JSON payload, and HTTP headers that provide context. Since there is no way to get these webhook HTTP headers from API Gateway to SQS using "Action=SendMessage" in Mapping template (correct me if my assumption is wrong...I have tried http headers in method request and integration request), I have included the headers from webhook in the API Gateway mapping template as follows:
Action=SendMessage&MessageGroupId=$input.params('MessageGroupId')&
MessageBody={
"Header1":"$input.params('Header1 key')",
"body":"$input.json('$')"
}
My code in Lambda function is as follows:
'use strict';
exports.handler = async function(event, context) {
if (event.Records) {
event.Records.forEach(record => {
var rec = JSON.parse(JSON.stringify(record.body));
console.log("Record body: " + rec);
console.log("Header1: " + rec.Header1);
});
}
return {};
};
The value of 'rec' in CloudWatch log is rather quite long but here is the simplified version:
Record body: {
"Header1":" OYqmDnuUpDs8oF1RwoBnJywBY6c1I4qLklU=",
"body":"{"id":820982911946154508,"email":"jon#doe.ca","closed_at":null,"created_at":"2020-11-30T20:25:43-05:00","updated_at":"2020-11-30T20:25:43-05:00","number":234,"note":null,"token":"123456abcd","gateway":null,"test":true,"total_price":"7.00","subtotal_price":"-3.00"
}
My question - how can I read the values of Header1 and body? I have tried rec.Header1, rec[0].Header1 and rec["Header1"] etc. and I get "Undefined" in all the cases. Thanks.
The body is already stringified.
You need to simply call JSON.parse(record.body) to parse the string to valid JSON. Note that this could throw an error.

How to use the full request URL in AWS Lambda to execute logic only on certain pages

I have a website running on www.mywebsite.com. The files are hosted in an S3 bucket in combination with cloudFront. Recently, I have added a new part to the site, which is supposed to be only for private access, so I wanted to put some form of protection on there. The rest of the site, however, should remain public. My goal is for the site to be accessible for everyone, but as soon as someone gets to the new part, they should not see any source files, and be prompted for a username/password combination.
The URL of the new part would be for example www.mywebsite.com/private/index.html ,...
I found that an AWS Lambda function (with node.js) is good for this, and it kind of works. I have managed to authenticate everything in the entire website, but I can't figure out how to get it to work on only the pages that contain for example '/private/*' in the full URL name. The lambda function I wrote looks like this:
'use strict';
exports.handler = (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
// Continue request processing if authentication passed
callback(null, request);
return;
}
// Configure authentication
const authUser = 'USER';
const authPass = 'PASS';
// Construct the Basic Auth string
const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');
// Require Basic authentication
if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
callback(null, response);
}
// Continue request processing if authentication passed
callback(null, request);
};
The part that doesn't work is the following part:
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
// Continue request processing if authentication passed
callback(null, request);
return;
}
My guess is that the request.uri does not contain what I expected it to contain, but I can't seem to figure out what does contain what I need.
My guess is that the request.uri does not contain what I expected it to contain, but I can't seem to figure out what does contain what I need.
If you're using a Lambda#Edge function (appears you are). Then you can view the Request Event structure here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-request
You can see the actual value of the request URI field by using console.log and checking the respective logs in Cloudwatch.
The problem might be this line:
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
If you're strictly looking to check if a JavaScript string contains another string in it, you probably want to do this instead:
if (!request.uri.toLowerCase().indexOf("/private/") !== -1) {
Or better yet, using more modern JS:
if (!request.uri.toLowerCase().includes("/private/")) {

How to get the query parameters in nock callback

I want to access the query parameter in nock reply callback.
The request object that is exposed contains the path that has them as a string. But I would like to access them as a map so that I will not have to deal with parsing the string
const scope = nock('http://www.google.com')
.get('/cat-poems')
.reply(function(uri, requestBody) {
console.log('path:', this.req.path)
console.log('headers:', this.req.headers)
// ...
})
I would expect the query params to be a separate map that I can access
Does anyone know of a way to achieve this?
The value of this.req inside a reply function is an instance of a ClientRequest that has been slightly modified.
Unfortunately for your use case, ClientRequest does not provide an easy way to access just the query params. But you do have access to the full path, from which you can parse the query params out.
const nock = require('nock')
const http = require('http')
const url = require('url')
const scope = nock('http://www.google.com')
.get('/cat-poems')
.query(true)
.reply(function(uri, requestBody) {
const parsed = new url.URL(this.req.path, 'http://example.com')
console.log('query params:', parsed.searchParams)
return [200, 'OK']
})
const req = http.get('http://www.google.com/cat-poems?page=12')
// output >> query params: URLSearchParams { 'page' => '12' }
The object being logged is a URLSearchParams instance.
Using the URL constructor is the preferred method over url.parse now, so I've used that for the example. Keep in mind that URL won't parse relative paths alone, it requires an origin, but since you don't care about the host in the end it can be a dummy value (hence the use of "example.com").

How to construct and extract value from simple HTTPS request in Node.js?

I have a simple HTTPS request -
https://api.pro.coinbase.com/products/btc-eur/ticker
In the browser this returns one object. What's the simplest code that will allow me to retrieve and display this object (as is) in the terminal of Node?
const https = require('https')
const url = https.get('https://api.pro.coinbase.com/products/btc-eur/ticker')
const myObject = JSON.parse(url)
console.log(myObject)
A simple copy / paste of the above code in VSC returns the error SyntaxError: Unexpected token o in JSON at position 1.
#mamba76, welcome to the SO community. Please use Node.js node-fetch package. It is much simpler to use. You can install it using npm install.
Following code might help:
"use strict";
const fetch = require('node-fetch')
async function getValue() {
// Invoke the API.
// Wait until data is fetched.
let response = await fetch('https://api.pro.coinbase.com/products/btc-eur/ticker');
let value = await response.json();
return value;
}
getValue().then(result => {console.log(result.price);});
As a good practice, always assume that API calls over the HTTP (whether in your own network or outside) might take time to return data and hence you should use async-await pattern to make these requests.
Extending #Akshay.N's answer and without using external dependencies,
const https = require('https')
https.get("https://api.pro.coinbase.com/products/btc-eur/ticker",res=>{
let body = '';
res.on('data', (chunk) => { body += chunk; });
res.on('end', () => {
const myObject = JSON.parse(body);
console.log(myObject);
})
})
Now, what we're doing here is waiting on the data event as long as the data is coming in, and appending it to the variable body. Once the end event is encountered, we take that as a signal that all data has been received and we can proceed to parse the body into an object using JSON.parse (assuming the data was serialized in JSON; if it wasn't JSON.parse will throw an error).
This tutorial is helpful: https://davidwalsh.name/nodejs-http-request
try something like this:-
https.get("https://api.pro.coinbase.com/products/btc-eur/ticker",res=>{
res.on('data', (chunk) => { console.log(JSON.parse(chunk))})
})
With node (you need request module) :
// display object
(require("request")).get({
url: "myurl",
json: true
}, function(e,r,b){
console.log(b);
});
// display as string
(require("request")).get({
url: "myurl",
json: false
}, function(e,r,b){
console.log(b);
});
With just curl in your terminal (without node)
curl myurl

Resources