Angular POST request - error reading response - node.js

I have a rather simple situation. I make a call to an endpoint in my Google Cloud Functions, it sends a simple response. I try to console.log.
Angular component
async submitHandler(formDirective: FormGroupDirective) {
this.loading = true;
const formValue = JSON.stringify(this.reqForm.value);
this.http.post('https://myApp.cloudfunctions.net/myEndpoint', formValue)
.subscribe(res => {
console.log(res);
this.success = true;
formDirective.resetForm();
},
(err) => { console.log(err) });
this.loading = false;
}
Google Cloud Function
const functions = require('firebase-functions');
const cors = require('cors')({ origin: true });
const admin = require('firebase-admin');
admin.initializeApp();
exports.myEndpoint = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
const body = JSON.parse(req.body);
const name = body.name;
console.log("REQUEST BODY >>> ", body);
console.log("REQUEST NAME >>> ", name);
if (!name) {
res.status(400).send('missing name!')
}
res.send(name.split('').reverse().join(''))
});
});
The cloud function responds with the name field reversed. So, "name": "Kenny" would respond with "ynneK". That's what I should see in my front end when I console.log(res).
Here's my front end error:
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "https://myApp.cloudfunctions.net/myEndpoint", ok: false, …}
error: {error: SyntaxError: Unexpected token l in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttp…, text: "llaH ynneK"}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for https://myApp.cloudfunctions.net/myEndpoint"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "https://myApp.cloudfunctions.net/myEndpoint"
__proto__: HttpResponseBase
As you can see in the second line of "error: {.." I have text: "llaH ynneK"}. So, I don't think it's a server problem, I think it's a local Angular problem.
What am I doing wrong? Should I set response headers to application/json in the cloud function endpoint? I'm not sure what I'm doing wrong here.
The flow is:
JSON.stringify form data and send in post request (seemed to be the only way it'd work)
GCF endpoint gets it and JSON.parse(req.body)
endpoint does it's thing, then sends back to client res.send()
Client gets response to make sure all is well, but the error occurs when trying to read it.
Thanks for any help.

Related

Nodejs + Axios Post returns undefined value

I am using React + NodeJS & Axios but have been trying to send a post request but experiencing difficulties.
The request seems to be posting successfully, but all actions at the nodejs server is returning in the "undefined" data value, even if the data is passed successfully shown in the console.
REACT
const fireAction = (data1, data2) => {
const data = JSON.stringify({data1, data2})
const url = `http://localhost:5000/data/corr/fire`;
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'AUTHCODE',
}
}
axios.post(url, data, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
fireAction("Oklahoma", "Small apartment")
NODE
app.post('/data/corr/fire', async (req, res) => {
try {
const data = req.body.data1;
console.log(data)
} catch(e) {
res.send({success: "none", error: e.message})
}
});
Result of node: "undefined"
I have added the following body parser:
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
I am not sure why this error is happening. I see there is similar questions to mine: however none of them are applicable as I'm using both express and body parser which is already suggested.
You're POSTing JSON with a content-type meant for forms. There's no need to manually set content-type if you're sending JSON, but if you want to manually override it, you can use 'Content-Type': 'application/json', and access the response in your route with req.body. If it does need to be formencoded, you'll need to build the form:
const params = new URLSearchParams();
params.append('data1', data1);
params.append('data2', data2);
axios.post(url, params, config);

Display image on Angular 10, where image is uploaded using Multer on Node.js server

I am using MEAN stack for my web application. I am using Multer to store images in a artImages folder on Node.js server. This is post request uses Multer to upload images and store image paths with other data in MongoDB.
const storage = multer.diskStorage({
destination: (req, file, callBack) => {
callBack(null, 'artImages/paintings')
},
filename: (req, file, callBack) => {
callBack(null, `painting&${Date.now()}&${file.originalname}`)
}
})
var upload = multer({ storage: storage })
router.post('/', upload.array('artFileLocations'), function(req, res) {
var paintingFileDesitnations;
const files = req.files
if(!files) {
res.sendStatus(500)
} else {
(new Paintings({ 'paintingName': req.body.artName, 'paintingFileLocations': req.files })).save()
.then((info) => res.send(info))
.catch((error) => console.log(error));
}
});
Now I am making a get request to MongoDB which gives data and file paths. Now I want to send the data as well as their respective images stored on server back to Angular. I have written this for the same:
router.get('/', function(req, res) {
Paintings.find({})
.then(paintings => {
imagePath = path.resolve(<path>, paintings[0].paintingFileLocations[0].path)
fs.readFile(imagePath, 'utf8', function (err, data) {
if (err) {
console.log(err);
}
console.log('Data: ', data);
res.send(data);
});
})
.catch(error => console.log(error))
});
Here, the request never consoles error, I did console data which is obviously a lot of gibberish.
On Angular client this is the response where the status says 200 but the response is HttpErrorResponse. I don't understand what is happening.
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:8080/api/paintings/getAll", ok: false, …}
error: {error: SyntaxError: Unexpected token � in JSON at position 0 at JSON.parse (<anonymous>) at XMLHtt…, text: "�PNG
↵↵
IHDR??���7sRGB���8…�$�I'O$vr�:�b�*FR�B#!0(�b&�x'6��IEND�B`�"}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for http://localhost:8080/api/paintings/getAll"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "http://localhost:8080/api/paintings/getAll"
__proto__: HttpResponseBase
Can someone help undertand and solve this problem? And give me an idea of how to display the image on Angular client? Have been stuck on this for days.
If you're using Angular's HttpClient, the default responseType is json - you might want to choose blob
responseType?: "arraybuffer" | "blob" | "text" | "json"
See the overloads of the HttpClient get request
The call would be something like:
return this.httpClient.get(url, {responseType:'blob'});

PUT doesn't work on POSTMAN but okay when called from front-end

I have the following lambda function that works when called from the front-end React app:
// Update a contact
module.exports.updateContact = async (event, _context) => {
const id = event.pathParameters.id;
const body = JSON.parse(event.body);
const paramName = body.paramName;
const paramValue = body.paramValue;
const params = {
Key: {
id: id
},
TableName: contactsTable,
ConditionExpression: 'attribute_exists(id)',
UpdateExpression: 'set ' + paramName + ' = :v',
ExpressionAttributeValues: {
':v': paramValue,
},
ReturnValues: 'ALL_NEW'
};
try {
const res = await db.update(params).promise();
}
catch (err){
console.log(err);
return err;
}
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
status: 'updated!',
[paramName]: paramValue,
}),
};
return response;
};
But it fails with status code 502 & 'InternalServerErrorException' message when called from Postman.
I tried to solve it with one of the suggestions found on stackoverflow with method override:
It doesn't work and I'm now getting status code 403 and 'MissingAuthenticationTokenException'.
I'm not sure what I'm doing wrong, seeking some guidance. Thank you.
It seems the API endpoint has an Authorizer configured on the API Gateway and the front end is sending the correct credentials, while the Postman configuration is not.
I would suggest to use the browser dev tools selecting the PUT request and copy it as cUrl:
then you can paste the copied cUrl command into the Postman request. It will prefill all the parameters: method, body and the headers.
hope this help

Handling errors in aws lambda in serverless

I'm trying to return errors from my lambda functions but for all of the errors it just returns status 502 with message Internal server error. Previously it was just returning cors error for all types of returned errors. After adding 'Access-Control-Allow-Origin' : '*' in api gateway responses, i'm getting 502 error. I've logged thrown errors in catch block & i can see the specific errors in CloudWatch. I've seen this question but that didn't help anyway. Please note that instead of using callback i'm using async await. Also i've tried with & without lambda-proxy integration but the response is same. Do i need to configure something else in case of lambda-proxy?
const test = async (event, context) => {
try {
context.callbackWaitsForEmptyEventLoop = false;
const error = { status: 400, message: 'my custom error' };
throw error;
} catch(error) {
console.log('==== error ====', error);
return createErrorResponse(error.status || 500, error.errors || error.message);
}
createErrorResponse
const createErrorResponse = (statusCode, message, stack={}) => ({
statusCode,
headers: {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
error: message
}),
stack: JSON.stringify({ stack })
});
export default createErrorResponse;
serverless.yml
test:
handler: api/index.test
timeout: 360
events:
- http:
path: test-lambda-api
method: post
cors: true
When using Lambda proxy integration with API Gateway, the response from Lambda is expected in a certain format:
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
Include your stack JSON key inside the body itself. You can also refer to this post for more info: https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/
Also, you should enable debug logs for the API Gateway stage which will give a better understanding of what is sent and received from the API GW integration.

Using images from Google Street View Static API without exposing key

I am trying to include Street View images on a React app. Here is the documentation for the API:
Street View Static API
When I make a simple fetch request to google I get a response that contains a URL to the street view image as a JPG file:
bodyUsed:false
headers:Headers
ok:true
redirected:false
size:0
status:200
statusText:"OK"
Symbol(Body internals):Object {body: PassThrough, disturbed: false, error: null}
Symbol(Response internals):Object {url: "https://maps.googleapis.com/maps/api/streetview?lo…", status: 200, statusText: "OK", …}
timeout:0
// URL BELOW HERE IS WHAT I'M USING
url:"https://maps.googleapis.com/maps/api/streetview?location=3737%20N%20Southport%20Ave,%20Chicago,%20IL%2060613&key={MY_SECRECT_KEY}&size=300x300"
I can't use this image URL directly in my app because it contains my secret API key.
What I'm attempting to do now is use an AWS lambda function that uses Node to read the image file from the URL and then send this image file as an HTTP response to the client's react app.
I'm having trouble figuring out how to do this in Node. I've seen some code online about using Node's readFile function from the file system module. But I'm having trouble getting it to work.
Here's the code in my Lambda function.:
const fetch = require('node-fetch');
const fs = require('fs');
const autoCompURL = "https://maps.googleapis.com/maps/api/streetview?"
const { G_PLACES_KEY } = process.env
const key = `&key=${G_PLACES_KEY}`
const size = "&size=300x300"
function getSearch(e) {
const resp = JSON.parse(e.body)
return resp.place_address
}
async function googleResults(str) {
const response = await fetch(
`${autoCompURL}location=${str}${key}${size}`
)
return new Promise(resolve => resolve(response));
}
exports.handler = async function(event, context) {
try {
const userSearch = getSearch(event)
const autoResults = await googleResults(userSearch)
const imgURL = autoResults.url
const img = await fs.promises.readFile(imgURL)
if (autoResults.status !== "OK") {
// NOT res.status >= 200 && res.status < 300
return { statusCode: autoResults.status, body: autoResults.statusText, error_message: autoResults.error_message }
}
return {
statusCode: 200,
headers: {'Content-Type': 'image/jpeg'},
body: img
}
} catch (err) {
console.log(err) // output to netlify function log
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }) // Could be a custom message or object i.e. JSON.stringify(err)
}
}
}
Appreciate any clues on how I can get this to work.
Did you try this option ?
You can generate Digital Signature and use it along with API invocation. And limit unsigned requests to zero .
https://cloud.google.com/blog/products/maps-platform/google-maps-platform-best-practices-securing-api-keys-when-using-static-maps-and-street-view-apis

Resources