JavaScript JSON object not parsing correctly - javascript

Folks,
I keep beating my head against this problem. In essence, I have a form which submits a string value to search through DynamoDB RangeKey.
If the form contains "NEW Y" the function seems to break. If I stick "NEW%20Y", things work as expected. I see the following error if the space is used:
error: socket hang up
Somewhere in this code it must not be passing spaces correctly. What is baffling that 'some' spaces work, some dont.
exports.nameSearch = function (req, res) {
getJSON = function(options, onResult){
//console.log("rest::getJSON");
var prot = options.port == 8443 ? https : http;
var req = prot.request(options, function(res)
{
var output = '';
console.log(options.host + ':' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function (chunk) {
output += chunk;
});
res.on('end', function() {
var obj = JSON.parse(output);
onResult(res.statusCode, obj);
});
});
req.on('error', function(err) {
res.send('error: ' + err.message);
});
req.end();
};
if (!req.session.username) {
res.redirect('/login');
} else {
options = {
host: 'api.me.com',
port: 8443,
path: '/usr/name/'+req.body.name,
method: 'GET',
rejectUnauthorized: false,
requestCert: true,
agent: false,
headers: {
'Content-Type': 'application/json'
}
};//options
getJSON(options,function(statusCode, result) {
console.log("onResult: (" + statusCode + ")" + JSON.stringify(result));
// Check if we found anything
status = JSON.stringify(result.Count)
if (status == 'undefined') {
console.log ('Nothing Found by the name,', req.body.name)
var count = '0'
res.render('user/nameSearch', { title: 'title', count: count, req: req });
} else {
results = JSON.stringify(result)
console.log ("results 1 ok , ", results)
results = JSON.parse(results)
console.log ("results 2 ok , ", results)
res.render('base/nameSearch', { title: 'title', baseResults: results, req: req })
}
});// JSON
} // else
};

If the form contains "NEW Y" the function seems to break. If I stick "NEW%20Y", things work as expected.
That means somewhere you are url decoding your input, so you need to figure out which middleware does this. Possibly the built-in unescape function. But as it stands your question is too general for me to narrow down where urlencoded input is expected.

Related

Fetch always returns 200 even when server returns 400 or some error. How to get correct result?

The bounty expires in 2 days. Answers to this question are eligible for a +100 reputation bounty.
Ganesh Putta is looking for an answer from a reputable source.
I have below function in frontend, I have to make some api call here in this function. When i checked the called function via command prompt, the server api call returns 400. but the function in frontend fetch always returning 200 instead of error.
Thanks in advance.
Please guide to how to correct response which is thrown from server call?
Here is the frontend function.
function processFile() {
var fileToLoad = document.getElementById("fileToLoad").files[0];
var url;
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
var rows = fileLoadedEvent.target.result.split("\n");
for (var i = 0; i < rows.length - 1; i++) {
var cells = rows[i].split(",");
//alert(cells);
console.log("AgentteamID=" + cells[0] + " SkilltargetID=" + cells[1] + " flag=" + cells[2]);
url = "/updateAgentTeam?agentTeamID=" + cells[0] + "&skillTargetID=" + cells[1] + "&flag=" + cells[2],
//alert(url);
console.log("URL=" + url);
const response = fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}).then(response =>
response.json().then(data => ({
data: "success",
status: response.status
})).then(res => {
var statusCode = JSON.stringify(res.status);
//$('#Success_comment').html(JSON.stringify(res)+"---"+url;);
//alert(url);
document.getElementById("Success_comment").value += JSON.stringify(res) + "---" + url;
console.log("final result " + JSON.stringify(res) + "---" + url);
}))
}
}
fileReader.readAsText(fileToLoad, "UTF-8");
}
Here is the Server side code. For now we are using only Flag A block
app.get("/updateAgentTeam", (req, res) => {
var skillTargetID = req.query.skillTargetID;
console.log("req.query.skillTargetID =" + req.query.skillTargetID);
var agentTeamID = req.query.agentTeamID;
var flag = req.query.flag;
var finalurl = "http://198.18.133.11/unifiedconfig/config/agentteam/" + agentTeamID;
var xml;
//console.log("finalurl ="+finalurl);
axios({
url: finalurl,
method: "get",
auth: {
username: "xxx",
password: "yyy"
},
})
.then(async(response) => {
xml = response.data;
//console.log("after calling xml is "+JSON.stringify(xml));
res.send(response.data);
if (flag === 'D') {
agentremovedXML = removeAgentFromXML(xml, skillTargetID);
//newxml=removeAgentFromXML(xml);
//console.log("Final Agent remove XML "+JSON.stringify(agentremovedXML));
postAgentToTeam(agentTeamID, agentremovedXML);
//setSuccessJSON();
} else if (flag === 'A') {
AgentXML = await generateAgentXML(skillTargetID);
console.log("Returned agent xml is " + JSON.stringify(AgentXML));
console.log("xml where agent to be add " + JSON.stringify(xml));
if (JSON.stringify(xml.agentCount) == 0) {
AgentXML = {
"agent": [AgentXML]
};
xml.agents = [AgentXML];
console.log("xml with zero agents " + JSON.stringify(xml));
} else {
xml.agents[0].agent.push(AgentXML);
console.log("Compare " + JSON.stringify(xml));
}
console.log("xml send to postAgentToTeam is " + xml);
postAgentToTeam(agentTeamID, xml);
//postAgentToTeam(agentTeamID,agentXML);
}
})
.catch((err) => {
res.status(400);
res.send(err.response.status);
console.log(err);
//setErrorJSON()
});
});
async function postAgentToTeam(agentTeamID, xml) {
var teamurl = "http://198.18.133.11/unifiedconfig/config/agentteam/" + agentTeamID;
//console.log("final XML is "+JSON.stringify(xml));
xml.agents = xml.agents[0].agent.map(agent => ({
agent: agent
}));
var updatedJSONwithAgentTags = JSON.stringify(xml, null, " ");
//console.log("newwwwwwwwwwwwwwwwwwwwww "+ updatedJSONwithAgentTags);
//return;
js2xml = json2xml(JSON.parse(JSON.stringify(xml)));
json2 = "<agentTeam>" + js2xml + "</agentTeam>";
//console.log("newwww converted XML "+json2);
response = await axios({
url: teamurl,
method: "put",
auth: {
username: "administrator#dcloud.cisco.com",
password: "C1sco12345"
},
data: json2,
headers: {
'Content-Type': 'application/xml; charset=utf-8'
}
})
.then(response => {
console.log("hellooo " + response.status);
})
.catch((err) => {
console.log(err.response.data.apiError);
console.log("error res is " + err.response.status);
});
}
There's a quite few things wrong.
Inside your first .then(async(response) => {, you set the xml to response.data then right away call res.send(response.data) (200) which is the end of the execution order for Express. However, you go on to do a bunch of other things after the res.send (200) is sent. You should not do that.
You need to restructure your code. Starting from the top-down keeping in mind that all promises need to be returned and that res.send() is the end of the block. Throwing a 400 after this won't matter since Express already sent the response to the client.
You're also mutating theresponse output from Axios which is bad practice. Create a new one if you need to mutate it:
const newResponse = { ...response };
Additionally, this mixing of .then().catch() with an initial await is not how you should handles promises and would cause problems.
Don't write them like this:
response = await axios({}).then().catch().
Either do this:
axios({ // <--- no await, no let response =
url: finalurl,
method: "get",
auth: {
username: "xxx",
password: "yyy"
},
})
.then(async(response) => {
// res.send, etc.
}).catch((e) => {
// handle errors
});
Or use try / catch blocks like this:
try {
const results = await axios({
url: teamurl,
method: "put",
auth: {
username: "administrator#dcloud.cisco.com",
password: "C1sco12345"
},
data: json2,
headers: {
'Content-Type': 'application/xml; charset=utf-8'
}
}); // <--- the end.
console.log({results});
} catch (e) {
// handle errors
console.log(e);
}

Postman post request works but ajax post does not. Have checked client side js over and over

first question ever on stackoverflow and boy do I need an answer. My problem is that I have an endpoint to create an item, and it works when I send a POST request with Postman. I'm using node and express:
router.post("/", jwtAuth, (req, res) => {
console.log(req.body);
const requiredFields = ["date", "time", "task", "notes"];
requiredFields.forEach(field => {
if (!(field in req.body)) {
const message = `Missing \`${field}\` in request body`;
console.error(message);
return res.status(400).send(message);
}
});
Task.create({
userId: req.user.id,
date: req.body.date,
time: req.body.time,
task: req.body.task,
notes: req.body.notes
})
.then(task => res.status(201).json(task.serialize()))
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
That endpoint works when I send with Postman and the req body logged with the right values.
But when I send my ajax request, my server code logs the req.body as an empty object ('{}'). Because Postman works I believe the problem is with my client side javascript but I just cannot find the problem. I and others have looked over it a million times but just can't find the problem. Here is my client side javascript:
//User submits a new task after timer has run
function handleTaskSubmit() {
$(".submit-task").click(event => {
console.log("test");
const date = $(".new-task-date").text();
const taskTime = $(".new-task-time").text();
const task = $(".popup-title").text();
const notes = $("#task-notes").val();
$(".task-notes-form").submit(event => {
event.preventDefault();
postNewTask(date, taskTime, task, notes);
});
});
}
function postNewTask(date, taskTime, task, notes) {
const data = JSON.stringify({
date: date,
time: taskTime,
task: task,
notes: notes
});
//Here I log all the data. The data object and all its key are defined
console.log(data);
console.log(date);
console.log(taskTime);
console.log(task);
console.log(notes);
const token = localStorage.getItem("token");
const settings = {
url: "http://localhost:8080/tasks",
type: "POST",
dataType: "json",
data: data,
contentType: "application/json, charset=utf-8",
headers: {
Authorization: `Bearer ${token}`
},
success: function() {
console.log("Now we are cooking with gas");
},
error: function(err) {
console.log(err);
}
};
$.ajax(settings);
}
handleTaskSubmit();
What I would do:
Change header 'application/json' to 'application/x-www-form-urlencoded' since official docs have no info on former one.
Stop using $.ajax and get comfortable with XHR requests, since jquery from CDN is sometimes a mess when CDN get's laggy and XHR is a native implement and available immediately. Yes it's a code mess, but you always know that it is not the inner library logic thing, but your own problem. You blindly use library, that conceals XHR inside and you do not know how to ask the right question "XHR post method docs" because you are not yet comfortable with basic technology underneath.
Save this and import the variable
var httpClient = {
get: function( url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString = '';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
}
if (queryString.length !== 0) {
url += (url.indexOf('?') === -1 ? '?' : '&') + queryString;
}
xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.send(null);
},
post: function(url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString='';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
} else {
queryString=data
}
xhr.open('POST', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(queryString);
}
};
usage is as simple as jquery: httpClient.post(Url, data, (xhr) => {})
Check if you have body parser set-up in app.js
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({ extended: true })); // get information from html forms
if body parser is set-up try changing header to 'multipart/form-data' or
'text/plain'.
For just the sake check req.query
Cheers! :)

.ajax call never returns

I'm using Express and Node. I've got some code that is posting JSON to another service that is adding it in a database.
The logic is doing what it is supposed to do, but the ajax call I am making is never returning, in the 'Network' tab in Chrome Dev tools it always shows as 'Pending' and eventually errors out with net::ERR_EMPTY_RESPONSE.
Can anyone tell me where I am going wrong?
Ajax Call
$.ajax
({
type: "POST",
url: "/order",
contentType: 'application/json',
data: orderDataJson,
success: function () {
alert("success!");
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("Something went wrong checking out!\n" + textStatus + "\n" + errorThrown);
}
});
This above routes to /order, which in turn posts the data to the other service:
app.post("/order", function(req, res, next)
{
var options = {
uri: endpoints.ordersUrl + "order",
method: 'POST',
json: true,
body: req.body
};
request(options, function(error, response, body) {
if (error !== null )
{
return;
}
if (response.statusCode == 200 && body != null && body != "")
{
if (body.error)
{
res.status(500);
res.end();
return;
}
res.end(); // UPDATED AFTER COMMENT
return;
}
console.log(response.statusCode);
});
});
This is the relevant code in the other service (it's correctly adding the content in the database)
if (request.method == 'POST')
{
switch (path)
{
// Add a new order
case "/order":
var body = '';
request.on('data', function (data) {
body += data;
});
request.on('end', function () {
var orderData = JSON.parse(body);
// Insert into orders table
var saleDate = getDate();
var ordersQuery = "INSERT into orders (customerId, saledate)" +
" VALUES (" + orderData.customerId +",'" + saleDate + "')";
db.query(ordersQuery, function(err, result)
{
if (err)
{
throw err;
}
var orderId = result.insertId;
// Insert order details
for (var i=0; i < orderData.order.length; i++)
{
var productId = orderData.order[i].productId;
var quantity = orderData.order[i].quantity;
var orderDetailsQuery = "INSERT into orderdetails (orderID, productID, quantity)" +
"VALUES (" + orderId + "," + productId + "," + quantity +")";
db.query(orderDetailsQuery, function(err, result)
{
if (err)
{
throw err;
}
});
}
});
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
});
});
break;
Try to add this in your error block:
if (error !== null ) {
res.status(500).send('Internal server error!');
return;
}
I got this fixed. The issue seem to be that express 'middleware' function should have been:
app.post("/order", function(req, res, body)
as opposed to:
app.post("/order", function(req, res, next)
From the expressjs docs:
If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.

How do I tie together 2 asynchronous request.post calls in Express and NodeJS?

I'm still learning how to make calls with using Express. Single asynchronous calls are no problem, but now I have an example where I want to feed the result of one call into the next one.
My current code can be seen below, a really messy way that includes a forloop like function, a foreach and a Timeout. I'd like to learn the proper way to do this. Preferably something that scales.
The first call fills up a result object getWorkbooksResponse with a list of workbooks (incl. workbook id's and workbook names). The second part fires of a getViews call for each workbook in that list. The function checkResponse sorts through the views and puts them in alphabetical order by name.
What is the proper way to tie together 2 request.post calls?
I've been looking at next(), bluebird, async,... but some examples would definitely help.
var express = require('express');
var request = require('request');
var router = express.Router();
//initialize values
var workbookId = -1;
router.get('/workbooks/views', function(req, res) {
var workgroup = req.query.workgroup;
var authToken = req.query.auth_token;
var serverUrl = req.query.server_url;
//Assemble body for POST request...
var requestedBody = {
method: 'getWorkbooks',
params: {
page: {
startIndex: 0,
maxItems: 999
},
filter: {
operator: 'and',
clauses: []
}
}
};
//Send POST request...
request.post({
url: 'https://' + serverUrl + '/vizportal/api/web/v1/getWorkbooks',
body: JSON.stringify(requestedBody),
headers: {
'Cookie': 'workgroup_session_id=' + workgroup + '; XSRF-TOKEN=' + authToken,
'X-XSRF-TOKEN': authToken
}
}, function(err, response, body) {
body = JSON.parse(body);
var result = body.result;
if (result.errors) {
return res.json({
http_code: 401
});
} else {
getWorkbooksResponse = result;
var getViewsWorkbooksResponse = [];
var forloop = function(i) {
if (i < getWorkbooksResponse.totalCount) {
workbookId = getWorkbooksResponse.workbooks[i].id;
var workbookName = getWorkbooksResponse.workbooks[i].name;
request.post({
url: 'https://' + serverUrl + '/vizportal/api/web/v1/getViews',
body: JSON.stringify({
method: 'getViews',
params: {
page: {
startIndex: 0,
maxItems: 999
},
filter: {
operator: 'and',
clauses: [{
operator: 'eq',
field: 'workbookId',
value: workbookId
}]
}
}
}),
headers: {
'Cookie': 'workgroup_session_id=' + workgroup + '; XSRF-TOKEN=' + authToken,
'X-XSRF-TOKEN': authToken
}
}, function(err, response, body) {
body = JSON.parse(body);
var result = body.result;
if (result.errors) {
response = {
http_code: 401
};
} else {
result.views.forEach(function(view) {
view.workbookName = workbookName;
getViewsWorkbooksResponse.push(view);
});
}
});
forloop(i + 1);
} else {
var checkResponse = function() {
if (getViewsWorkbooksResponse) {
//Alphabetize Response array on view name
getViewsWorkbooksResponse.sort(function(a, b){
return a.name.localeCompare(b.name);
});
return res.json({
http_code: 200,
response: getViewsWorkbooksResponse
});
}
};
setTimeout(checkResponse, 1000);
}
};
if (getWorkbooksResponse.totalCount) {
forloop(0);
}
}
});
});
module.exports = router;
With promises it would be the simplest way:
You wrap your request.post calls in
new Promise((resolve,reject)=>
{ ...
request.post(...,function(err,response, body){
if (result.errors){reject(/* error info here */)}
else {resolve(/* data goes here */)}
}
});
And then use
Promise.all[promise1,...,promiseN].then(function(result){
//here is where you gonna do things after all requests are done
})

Nodejs https not working properly

I am making an api request to facebook using this:
var hash = crypto.createHmac('sha256', config.FACEBOOK_SECRET).update(access_token).digest('hex');
console.log('hash = ' + hash);
var options = { //start of options
hostname: 'graph.facebook.com',
port: 443,
//path: '/oauth/?appsecret_proof='+hash+'&access_token='+at,
path: '/v2.6/me?fields=id,email,name&access_token=' + req.body.access_token + '&appsecret_proof=' + hash,
method: 'GET'
}; //end of options
var fbResponse = [];
var req = https.request(options, (response) => {
console.log('statusCode: ', response.statusCode);
console.log('headers: ', response.headers);
response.on('data', (d) => {
console.log(d)
fbResponse.push(d);
});
response.on('end', () => {
console.log('No more data in response. ');
res.send(JSON.stringify({
r: fbResponse
}));
return;
})
});
req.on('error', (e) => {
console.error(e);
res.status(500).send(JSON.stringify({
r: 'error'
}));
return;
});
req.end();
This return something like this in the response:
{"id":"123456","email":"bob\u0040gmail.com","name":"bobby bobski"}
Notice how the email doesn't have the '#' symbol instead it has \u0040. I do not want this, I would like the # instead. I am not sure as I have not tested this, but I am thinking this will probably be occurring with all special characters. How do I make it so I get the actual character?
I found the answer. You need to parse the response and then stringify. It will then work.
console.log('This will now have the # and not the unicode : ' + JSON.stringify(JSON.parse(fbResponse)) );

Categories

Resources