Summary of Problem
I have a React Native application where I create a date object, assign it to startTime and send it via Axios to my backend end node.js server. When I typeof console.log the variable immediately after receiving it in the backend, the type is a string. I have done typeof console.logs in the front end to make sure it is a date object all the way up until it is sent via Axios to the backend.
This may be a axios or HTTPS thing I'm not familiar with as I'm a new developer. Any help would be greatly appreciate it. I've included all relevant code below but let me know if you think I should include more.
Code
React Native API Call
startTime console logs as an object
export const confirmDailyComp = async data =>
new Promise(async (resolve, reject) => {
const { userId, compName, startTime, competitors, lengthType, privacyType } = data; //FIXME: add competition back here, took it out to throw error on purpose
console.log('confirmDailyComp typeof startTime', typeof startTime) //this console logs as an object
try {
const response = await http.post("/v1/route", {
userId,
compName,
startTime,
competitors,
lengthType,
privacyType
});
const competition = response.data.newComp;
resolve(competition);
} catch (err) {
console.log("error fetching friends", err);
reject(err);
}
});
Node.js Controller
startTime console logs as a string
There is of course more code in the rest of the route but I didn't include it to be concise. I'm happy to include it if you think its necessary.
start: async function(req, res, next) {
try {
const {
competitors,
startTime,
userId,
compName,
privacyType,
lengthType,
seasonNum
} = req.body;
console.log("typeof startDate", typeof startTime); // console logs as a string
} catch(err) {
}
JavaScript Date is an object, and JSON specification doesn't have date.
This is why when you send it through JSON it will be serialized as string, because Axios by default will serialize it as UTC format string.
You have three options:
understand they are UTC string and you create a date object which can parse them correctly.
convert the date yourself to its numeric format.
The third option is the JSON.parse method has an optional parameter to pass your custom parser, which you can use it to build a custom date parser.
You can get an idea from this:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter
Try considering sending startTime as a timestamp(something like 1567114948292).
Now you can take this value in the server and get the time.
Related
I am trying to convert a javascript object array to JSON to pass it with the POST request to the cloud function. However when I use the JSON.parse() function I get an error:SyntaxError: Unexpected token u in JSON at position 0
Can someone tell me what I'm doing wrong, this is my POST request:
const body = `{
"item":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}]
}`;
const init = {
method: 'POST',
body
};
fetch('https://us-central1-web-app-bbo-prod.cloudfunctions.net/TestObject', init)
.then((response) => {
return response.json(); // or .text() or .blob() ...
})
.then((text) => {
// text is the response body
})
.catch((e) => {
// error in e.message
});
and this my cloud function:
exports.TestObject = functions.https.onRequest(async(request, response)=> {
var corsFn = cors();
corsFn(request, response, async function() {
const myJSON = request.body.item;
console.log(JSON.parse(myJSON))
//console.log(item)
})})
TLDR:
Server: remove JSON.parse
Client: change init to
const init = {
method:'POST',
body:JSON.stringify(body),
headers: {
'Content-Type':'application/json'
}
}
Explained:
You've got three issues I see.
Anytime you see the following you are calling JSON.parse on invalid stringified JSON.
SyntaxError: Unexpected token u in JSON at position 0
First, req.body should already be JSON if everything's working properly and you received JSON. To call JSON.parse(req.body.item) is to pass an Object to a function that takes strings. You can just refer to req.body.item.
Second you have to make sure you're sending the correct body, you are passing an object as the body for the fetch function. I'm not familiar with the fetch function but a quick look at the Mozilla examples indicate that should it should be stringified, i.e. body:JSON.stringify(body). The object you pass likely is either converted to form data or has toString() called on it and you're sending [object Object].
// REPL
> ({foo:'bar'}).toString()
'[object Object]'
Third, you have to make sure it gets parsed properly on the backend.
According to the GCloud nodeJS docs the nodejs google cloud functions implement an endpoint for an Express Router.
Normally if you wanted to use JSON the way you are you would need the body parser BodyParser middleware. The examples here (GCloud HTTP Functions) indicate that the body-parser middleware is already instead and working based off of Content-Type header.
Since it's deciding whether to parse it as JSON based on content type you want to set the header in your fetch function as 'content-type':'application/json'.
I'm doing an app with a part native and a part with html. I need to send the phone contacts to the html part, which later parse the JSON passed to a json object in Javascript with JSON.parse. Sometimes, it fails and I don't have anyway to know in which contact is failing.
Some advice to know the error? In Android or in javacript, thank you.
JS parse
return new Promise((resolve, reject) => {
(<any>window).contactsOk = function (contacts) {
try {
const data = JSON.parse(contacts);
resolve(data);
} catch (error) {
console.log('Error parsing contacts info', error);
reject(error);
}
}
Please check this post
Passing a JavaScript object using addJavascriptInterface() on Android
You can pass as primitive and String. Avoid passing JSON object to JavaScript
Convert Json object to String and then send
I am trying to send multiple objects in the response as json back to client from one route. It is some kind of middleware, that gets called, and then it calls itself another route inside to get the data and do some data processing. Here is the code:
const axios = require('axios');
var datetime = require('node-datetime');
function MiddlewareRoutes(router) {
var MiddlewareController = require('../controllers/MiddlewareController')
router.route('/Middleware/someotherLink/parametres').get(function(req,res,next) {
console.log(req.params.id, req.params.startTime, req.params.endTime);
url = `http://localhost:hidden/link/..`;
url2 = "http://localhost:port+params..."
axios.get(url) //, {responseType: 'json',}
.then(response => {
var formattedData = formatData(response.data);
[max,min] = getMinMax(formattedData);
res.write("max:",max);
res.write("min:",min);
res.write(formattedData);
res.end();
})
.catch(error => {
console.log(error);
});
})
}
However, I am getting the error:
TypeError: First argument must be a string or Buffer
at write_ (_http_outgoing.js:642:11)
at ServerResponse.write (_http_outgoing.js:617:10)
at axios.get.then.response (C:\Users\U500405\Desktop\Backend\routes\MiddleWare.js:19:13)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
What am I doing wrong? I cannot just send strings, because I have to send objects...
Write is for writing strings to the response body, the parameters accepted are (chunk[, encoding][,callback]), however an object is not a string, and your min/max values are not encodings.
As said before, you could use JSON.stringify to convert an object into a JSON string, however since this is pretty common behaviour Express provides a send method that can do exactly that.
res.write(JSON.stringify({
min,
max,
formattedData
}));
or
res.send({
min,
max,
formattedData
});
res.write(formattedData); Here formatted data is a object . As the error message says, write expects a string or Buffer object, so you must convert it. by doing so : res.write(JSON.stringify(formattedData)) . The node expects the content to not to be a object because it needs string to pass on to the server. The server only understands plain text input as mentioned in Nodejs Docs Nodejs Doc Link for res.write() , and by default the encoding is 'utf-8' . so When sending a object through the server , the server discards it and throws an error of expected buffer chunks or string data.
I am sending a base64 string to my node.js server. But its adding the string inside object brackets {}
Front-end code:
I encode the base64 string to preserve it.
let options = {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};
let encodedImage = encodeURIComponent(body);
return this._http.put<IUser>(`${BASE_URL}/api/users/${id}/photo`, encodedImage, options)
.map(data => {
return data;
})
.catch(err => {
// do whatever you want when error occurs
console.log(err);
// re-throw error so you can catch it when subscribing, fallback to generic error code
return Observable.throw(err || 'API_ERROR');
});
}
Serverside:
let upload = (req, res) => {
let b64string = req.body;
console.log(b64string);
}
The console log is:
{ 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQA.....+idkVe3H/9k=': '' }
How can I extract the base64 string from within the object so I can access it. it seems like a simple thing but my brain has given up. Any help will be greatly appreciated.
Update:
Update 2:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
There seems to be something putting your string in an JSON encoded value, hence the {'...' : '...'}.
You could put the base64 encoded string in an json object from the get go, that way you have an expected input when you get the object. You just need to get the string from the json key.
Hope this helps.
The problem here is that you creating a PUT request with
Content-Type : application/x-www-form-urlencoded but you pass a single string as your data. So the body-parser sees this string as a key with an empty value and that's why you get this console log.
So the solution to this,
let options = {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};
let requestBody = new URLSearchParams()
requestBody.append('encodedImage',body)
return this._http.put < IUser > (`${BASE_URL}/api/users/${id}/photo`, requestBody, options)
So we said we have a content type of form-urlencoded (that means all of your data will be passed as giant url query) and we passed a urlencoded form (through the URLSearchParams)
Please bear in mind URLSearchParams browser compatibility.
Please consider using multipart/form-data for your file uploads instead, if you don't have any limitations. The logic of the above will be the same except the new URLSearchParams() will be new FormData().
I've tried all sorts to get this to work. I'm trying to request a PDF from an API on node, then send this back to the client who called it to begin with.
For the minute I just want to successfully save and view the PDF on the node server.
The issue is the PDF file is always empty when I open it (Even though it has a size of 30kb).
The basic flow is like this (removed a few bits, but the below code works and returns me the PDF fine)
// We pass through session ID's, request dates etc through in body
app.post("/getPayslipURL", function(client_request, res) {
// create request, which will simply pass on the data to the database (In order to get the NI number we need for the pay API)
const NI_NUMBER_REQUEST = db_api.createRequestTemplate({
body: JSON.stringify(client_request.body)
});
// Create a chain of HTTPS Requests, Starting with our call to the DB
requestPromise(NI_NUMBER_REQUEST)
.then((db_response) => {
const PAY_API_OPTIONS = /*Code to generate options based on furhter DB info (Includes dates etc)*/
return requestPromise(PAY_API_OPTIONS); // Call pay API
})
.then((pay_pdf_data) => {
console.log(typeof pay_pdf_data); // It's a string
// At this point I can log pay_pdf_data, But if I try to save it to file it's always empty
// No matter how I encode it etc
fs.writeFile("./test.pdf", pay_pdf_data, 'binary', function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
})
.catch(err => `Error caught: ${console.log}`) // Catch any errors on our request chain
});
}
I've tried saving with / without the binary flag as suggested in other posts in both the file save aswell as within the requests itself. Also various types of decoding methods have been tried, I always get an empty PDF saved.
My return data looks like this (is much bigger, when saved as test.pdf I get a 30kb file as before mentioned)
%PDF-1.4
%����
1 0 obj
0 obj
<
I've found a post which says about piping the data all the way through, I have a feeling my pdf_data is corrupted when getting converted to a string
Any ideas how would I go about doing this with the current setup?
e/ RequestPromise is a library, could also use the standards request library if it's easier
https://github.com/request/request-promise -
https://github.com/request/request
Thanks!
Your code doesn't work because the underlying request library (used by request-promise) requires the option encoding set to null for binary data - see https://github.com/request/request#requestoptions-callback.
Here's how you download binary data using that module -
app.post("/getPayslipURL", function(client_request, res) {
const NI_NUMBER_REQUEST = db_api.createRequestTemplate({
body: JSON.stringify(client_request.body),
encoding: null
});
requestPromise(NI_NUMBER_REQUEST)
.then((db_response) => {
const PAY_API_OPTIONS = /*Code to generate options based on furhter DB info (Includes dates etc)*/
return requestPromise(PAY_API_OPTIONS); // Call pay API
})
.then((pay_pdf_data) => {
fs.writeFile("./test.pdf", pay_pdf_data, 'binary', (err) => {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
})
.catch(err => `Error caught: ${console.log}`) // Catch any errors on our request chain
});
}