Express JS sends undefined JSON variables to client - javascript

I'm working on learning Express JS and practicing security. I have moved some coupon code logic to the server, to make it more secure. That means that I send a coupon code to the server, it checks whether it is a valid code, and then returns a JSON object with a message and a win indicator (1 for the win, 0 for loss).
My issue is that I cannot figure out how to get both the message and the win indicator on the client from the response. It's always undefined
This is the client:
window.addEventListener("load", () => {
const nameInput = document.getElementById("name");
const couponCodeInput = document.getElementById("coupon-code");
const button = document.getElementById("button");
const nowinElem = document.getElementById("no-win");
const winElem = document.getElementById("you-won");
button.addEventListener("click", async e => {
e.preventDefault();
winElem.style.display = "none"; //new click of button:
nowinElem.style.display = "none"; // hide previous messages
let resp = await fetch('winner', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: nameInput.value, code: couponCodeInput.value })
});
if (resp.status !== 200) console.log('Winner report failed');
var res = resp.body;
if(res.win == "1"){
winElem.style.display = "block";
winElem.innerText = res.msg;
} else {
nowinElem.style.display = "block";
nowinElem.innerText = res.msg;
}
})
})
And this is the server:
const express = require('express')
const fs = require('fs')
const app = express()
const html = fs.readFileSync('coupon-client.html');
const js = fs.readFileSync('coupon-client.js');
const winnerCodes = ["123", "secret", "abc321"];
app.get('/', (req, res) => {
res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
res.end(html);
})
app.get('/coupon-client.js', (req, res) => {
res.writeHead(200, {"Content-Type": "application/javascript"});
res.end(js);
})
app.post('/winner', express.json(), (req, res) => {//use built-in JSON middle-ware
let jsonObj = req.body //JSON already parsed: { "name": "my name" }
if(winnerCodes.includes(jsonObj.code)){
console.log(`Congratulations to ${jsonObj.name}!`);
console.log("We'll send you a diploma.");
res.json({msg: 'Congratulations - and thanks!', win: '1'});
} else {
console.log(`Condolences to ${jsonObj.name}!`);
console.log("We'll send you nothing.");
res.json({msg: 'Sorry, you did not win - but thanks for playing!', win: '0'});
}
})
app.listen(8080, () => console.log('Listening...'))
As I mentioned above, I cannot get the JSON data object to give me the win and message variables. I have tried to do "JSON.parse(resp.body)" but then it gives me an error saying "unexpected character at....", which I read means that the body is already parsed, so I'm just taking the body now.
Can anyone give me a hint or help me out?

Reading a Response object works differently from how you currently are using it.
Use the json() method on the response object. It will return a promise which parses the response from JSON into usable data.
About the Response object and its usage.
Currently you're accessing the body property on the Response object, which it inherited from the Body mixin.
The body read-only property of the Body mixin is a simple getter used to expose a ReadableStream of the body contents. MDN
So you're accessing a ReadableStream object and try to read the win property from it, which doesn't exist on the stream.
To help with this the Body mixin has methods to convert the stream into usable data. Examples are Body.json() and Body.text(). These methods read the stream and convert the body into an object, array, string or number when it has a JSON structure or into a single string, which is useful when you're sending HTML or just raw text.
Both methods return Promises in which you have to wait for the result to be ready for usage.
let resp = await fetch('winner', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: nameInput.value,
code: couponCodeInput.value
})
});
if (resp.status !== 200) console.log('Winner report failed');
// Decode body from JSON.
let { win, msg } = await resp.json();
if (win === "1") {
winElem.style.display = "block";
winElem.innerText = msg;
} else {
nowinElem.style.display = "block";
nowinElem.value = msg;
}

Related

How do I receive multiple JSON objects and use each one seperately?

I am trying to receive JSON objects via a websocket server. And I receive one JSON object every second that I want to be able to use each one separately as it gives a status on whether someone is active or not. However, whenever I try and store all the JSON's into a data structure only the final JSON I receive will be stored the others get deleted.
So my question is: is there a way to store JSON into a data structure whilst receiving it?
This is the JSON I receive looks something like this:
{"studentNum":"21127985","active":false,"currentScore":0}
here is the code for receiving the json from the server:
const WebSocket = require('ws');
// const serverAddress = "ws://127.0.0.1:5000";
const serverAddress = ' *SERVER NAME*';
const ws = new WebSocket(serverAddress, {
headers: {
"user-agent": "Mozilla"
}
});
ws.on('open', function () {
});
ws.on('message', function (msg) {
fs = require('fs')
console.log(msg.toString())
var obj = JSON.parse(msg);
/* convert buff to string
const json = JSON.parse(file.toString())
json.push(msg);*/
var writerStream = fs.createWriteStream('output.json')
writerStream.write(msg, 'UTF-8')
writerStream.end();
writerStream.on('finish', function () {
});
writerStream.on('error', function (err) {
console.log(err.stack);
});
});
you can put fs outside of your WS message callback
const WebSocket = require('ws');
fs = require('fs');
var writerStream = fs.createWriteStream('output.json')
const serverAddress = "ws://127.0.0.1:5000";
const serverAddress = ' *SERVER NAME*';
const ws = new WebSocket(serverAddress, {
headers: {
"user-agent": "Mozilla"
}
});
ws.on('open', function () {
});
ws.on('message', function (msg) {
writerStream.write(msg, 'UTF-8')
writerStream.on('error', function (err) {
console.log(err.stack);
});
});
Or alternatively you can write file in chunks i.e. store 10 messages in an array and write it using fs.appendFile function.

Why is URL.creatObjectURL(blob) giving a cross-origin frame error in NodeJS/React application

I have never had this happen before and am not sure why it's happening.
I have a component written to display PDF files in an iframe as part of a larger application. I am retrieving a BLOB stream from the server and attempting to create a URL for it to display in the iframe but it keeps giving me a cross-origin error, which I thought would not be possible since it is creating the URL out of data.
Here is my entire component:
import React, { useState, useEffect } from 'react'
import IFrameComponent from '../Elements/IFrameComponent';
const PDFPages = (props) => {
let [file, setFile] = useState(null)
let [notFound, show404]=useState(false)
useEffect(() => {
let id=props.site?.componentFile;
fetch(`${process.env.REACT_APP_HOST}/documents/GetPDF`,
{
method: 'POST'
, headers: {
'Content-Type': 'application/json'
}
, credentials: 'include'
, body: JSON.stringify({file:id})
})
.then(async response => {
let blob;
try{
blob=await response.blob(); // <--- this functions correctly
}
catch(ex){
let b64=await response.json()
blob=Buffer.from(b64.fileData,'base64')
}
//Create a Blob from the PDF Stream
//Build a URL from the file
const str=`data:application/pdf;base64,${b64.fileData}`
const url=URL.createObjectURL(blob) //<--- ERROR IS THROWN HERE
setFile(url);
})
.catch(error => {
show404(true)
});
}, []);
if(!notFound){
return <IFrameComponent src={file} title=''>
Please enable iFrames in your browser for this page to function correctly
</IFrameComponent>
}
else {
return (
<>
<h3> File {file} could not be found on server</h3>
</>
)
}
}
export default PDFPages;
For completeness here is the GetPDF function from the server which is sending the file.
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
I have done very little work in node sending files but am positive my client code is good. Where am I going wrong here?
The problem was that I was trying to be too fancy sending a BLOB or Base64 data. After investigation I rewrote
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
on the server to
router.get('/GetPDF/:fileName', async (req, res, next) => {
let fileName = req.params.fileName
fileName = `./data/documentation/${fileName.replace(/%20/g, " ")}`;
try {
let data = await dataQuery.loadFile(fileName);
res.contentType("application/pdf");
res.send(data);
}
catch (ex) {
res.send({ error: true })
}
});
Then calling it from the client using
const url = `${process.env.REACT_APP_HOST}/documents/GetPDF/${props.site.componentFile}`
as the iFrame src sends the PDF properly as expected.
This same method also solved another problem with HTML pages sent from the server not functioning correctly.

Need help understanding why my fetch method is sending undefined

As the title says my fetch isn't doing what I want it to. Im trying to implement stripe into my grocery store website. I'm using a node.js server with express, and ejs to integrate with the front end. My client side js uses a fetch method to send a POST request to the server with all the information from the client side. The client side pulls data from a JSON file to access the store items. Those items are to be restructured as objects in the fetch and sent to the server to initiate the stripe checkout.
However, the fetch fails at the get go with a status 500. It claims that an unidentified was passed instead of a JSON. As a result, I tried to send back a hard coded object to see where the error was occurring but it also came back as undefined. I'm stumped and need any help I can get.
I'm new at coding/programming so I'm sure there is a lot wrong with my code. Thank you for your time. My code is below.
Client side JS
let payButton = document.getElementsByClassName("pay")[0].addEventListener("click", function() {
// alert("Payment recieved");
// let totalItemsInCart = document.getElementsByClassName("shopping-cart-item-div");
//
// let x = 0;
//
// while (x < totalItemsInCart.length){
// totalItemsInCart[x].remove();
// }
// updateCartTotal();
let items = [];
let cartCollection = document.getElementsByClassName("shopping-cart-basket")[0];
let cartItems = cartCollection.getElementsByClassName("shopping-cart-items");
for (let x = 0; x < cartItems.length; x++) {
let cartItem = cartItems[x];
let cartItemQuantity = cartItem.parentElement.getElementsByClassName("shop-item-input")[0];
let quantity = cartItemQuantity.value;
let id = cartItem.parentElement.dataset.itemId;
let nameText = cartItem.innerText;
let name = nameText.replace(/per lb|per item|per bundle/g, "").replace("$", "").replace(":", "");
let cartTotal = document.getElementsByClassName("shopping-cart-number")[0].innerText;
let price = parseFloat(cartTotal.replace("$", "")) * 100;
items.push({
id: id,
quantity: quantity,
name: name,
price: price
});
}
fetch("/create-checkout-session", {
method: "POST",
header: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: 1,
quantity: 2,
name: "test",
price: 500})
}).then(function(res) {
if (res.ok) {
return res.json();
} else {
return res.json().then(function(json) {
Promise.reject(json)
});
}
}).then(({url}) => {
console.log();
window.location = url;
}).catch(function(e) {
console.error("Error: " + e.error)
});
});
Sever side JS
app.post("/create-checkout-session", async function(req, res) {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
mode: 'payment',
line_items: req.body.items.map(function(item) {
return {
price_data: {
currency: 'usd',
product_data: {
name: item.name
},
unit_amount: item.price
},
quantity: item.quantity
}
}),
success_url: `${process.env.SERVER_URL}/success.ejs`,
cancel_url: `${process.env.SERVER_URL}/cancel.ejs`
})
res.json({
url: session.url
})
res.redirect(303, session.url)
} catch (e) {
res.status(500).json({
error: e.message
})
}
});
app.get("/success", function(req, res) {
res.render('success');
});
app.get("/cancel", function(req, res) {
res.render('cancel');
});
Server Side dependencies
require("dotenv").config();
const express = require('express');
const ejs = require('ejs');
const bodyParser = require('body-parser');
const path = require('path');
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(express.static('public'));
app.set('view engine', 'ejs');
EDIT: included my entire client side code for the checkout button. I originally left it out due to the code not being called anymore as I try to resolve the undefined issue. However, I got a few comments mentioning that items was not defined so I decided to add this in for clarity.
items is an empty array that has has objects pushed into it according to what ids were left in the shopping cart.
From MDN Docs
The Promise returned from fetch() won’t reject on HTTP error
status even if the response is an HTTP 404 or 500. Instead, as soon as
the server responds with headers, the Promise will resolve normally
(with the ok property of the response set to false if the response
isn’t in the range 200–299), and it will only reject on network
failure or if anything prevented the request from completing.
So what's happening here is that you're getting an error 500, so property res.ok is set to false, then it's not going through the conditional hence it's going to this part
return res.json().then(function(json) {
Promise.reject(json)
});
but you're not returning Promise.reject(json) because you're using an anonymous function, not an arrow function so you're returning undefinded, you must explicitly return the promise like this
return res.json().then(function(json) {
return Promise.reject(json)
});
^ Same goes for every anonymous function (you must return explicitly), instead of doing that I recommend you to use arrow functions and async-await

How can i parse this result from a fetch call EXpress NoseJs Javascript

Here is the code of my script, it's in the index.html file, I know it's wrong to be there but first I'm trying to make it work, then I'll move it.
readOperaciones();
async function readOperaciones(){
try{
const listaOr = document.getElementById('listaOrdenada');
const result = await fetch("http://localhost:8080/operaciones", {method: "GET"})
const operaciones = await JSON.parse(result)
//operaciones.forEach(t=>{
for (var i = 0; i < operaciones.length; i++) {
var row = operaciones[i];
console.log(row.codeemp);
}
/*tt = JSON.stringify(t);
const li = document.createElement("li");
li.textContent = tt.text;*/
/*t.forEach(cell=>{
const li = document.createElement("li")
li.textContent = cell.text;
li.id = cell.id;
})*/
//})
}
catch(e){
console.log("Error al leer las operaciones descriptas")
}
}
Here is the connecting with express
const {Client} = require('pg');
const express = require ("express")
const app = express();
app.use(express.json())
const client = new Client({
user: "postgres",
password: "1234",
host: "localhost",
port: 5432,
database: "webaduana",
})
app.get("/", (req, res) => res.sendFile(`${__dirname}/index.html`))
app.get("/operaciones", async (req, res) => {
const rows = await readAll();
res.setHeader("content-type", "application/json")
res.send(JSON.stringify(rows))
})
async function readAll(){
try{
const results = await client.query("select * from operaciones")
return results.rows;
}
catch(e){
console.log(e)
return [];
}
}
I don't know if I need to put more information but my question about all this code is here
I've tried many ways of doing it but I can't get those results in an ol element.
It doesn't give me any error, it just doesn't print anything in the HTML page
Add a .then to the fetch chain and print your results:
fetch('http://example.com/movies.json')
.then(response => {
console.log('response: ' + JSON.stringify(response));
})
...
...
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
you can use .json() method :
const fetched = await fetch("/url", {
method: 'GET',
});
const fetchedJson: object = await fetched.json();
console.log(fetchedJson)
There are a few ways to do that.
Using Promises
The fetch response object inherently includes some methods that can help you to get a response in different forms such as .json(), .text(), and .status. Learn more about it here. So, if you simply want to parse the answer into a JSON object, you can do
function doSomethingOnParsedJson(res) {
// Do something on the response here...
}
function readOperacions() {
fetch("http://localhost:8080/operaciones", {
method: "GET",
})
.then(res => res.json())
.then(doSomethingOnParsedJson) // Pass in a function without parentheses
.catch(console.error);
}
It's cleaner if you define a separate function which performs the job you want to do on the parsed response and pass the function (without parentheses) to then but you can also go ahead and give it a function directly like:
function readOperacions() {
fetch("http://localhost:8080/operaciones", {
method: "GET",
})
.then(res => res.json())
.then(parsedResponse => {
// do something...
})
.catch(console.error);
}
Using Async/Await
You can also use the async/await feature to achieve that.
function doSomethingOnParsedJson(res) {
// Do something on the response here...
}
async function readOperacions() {
try {
// Get the response from the server.
const res = await fetch("http://localhost:8080/operaciones", {
method: "GET",
});
// Parse it into a JSON object.
const parsedJson = res.json();
// Do something on it.
doSomethingOnParsedJson(parsedJson);
} catch (error) {
// Show an error if something unexpected happened.
}
}
Side Note
There is a neater way to send a JSON response in Express. You can use the .json method on the Express response object.
app.get("/operaciones", async (req, res) => {
const rows = await readAll();
/* Don't do this!
res.setHeader("content-type", "application/json")
res.send(JSON.stringify(rows))
*/
/* Do this instead ;-) */
res.json(rows);
})
Voila! It's that simple.

HTTP Request Using Axios

I am trying to crawl a website using NodeJS. I am making an HTTP request using Axios. I am able to only fetch those items which are available when webpage is loaded. All the HTML which is loaded when I scroll down further is not fetched.
Here is my code.
const axios = require('axios');
const cheerio = require('cheerio');
var request = require('request');
// table view
const url = "https://www.usnews.com/best-colleges/search?_sort=rank&_sortDirection=asc&study=Engineering&_mode=table";
fetchData(url).then((res) => {
const html = res.data;
const $ = cheerio.load(html);
const unilist = $('.TableTabular__TableContainer-febmbj-0.guaRKP > tbody > tr >td ');
unilist.each(function() {
let title = $(this).find('div').attr("name");
if (typeof(title) == 'string') {
console.log(title);
}
});
})
async function fetchData(url){
console.log("Crawling data...")
// make http call to url
let response = await axios(url).catch((err) => console.log(err));
if(response.status !== 200){
console.log("Error occurred while fetching data");
return;
}
return response;
}
I am trying to get all the university names. However, I am only able to get 13 universities because the others are loaded only when the page is manually scrolled down.
How do I access all the universities in the webpage: https://www.usnews.com/best-colleges/search?_sort=rank&_sortDirection=asc&study=Engineering&_mode=table
var request = require('request');
const url = "https://www.usnews.com/best-colleges/api/search?_sort=rank&_sortDirection=asc&_page=7&study=Engineering";
let options = {
url: url,
headers: {
"authority": "www.usnews.com",
"method": "GET",
//"path": `/best-colleges/api/search?_sort=rank&_sortDirection=asc&_page=6&study=Engineering`,
"scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"cookie": `ak_bmsc=60A136143B076291C93DD9728862F728172B301F314600004917B85E8498E04F~pl5NwmZFHheJnzgnDGIGpBb4YDDOuhPDVqrNGDysdm/dDPzFJis9zP1awrKKsxeJBlvqZWW6E3ssLbAdi/nUkIEkEiVPu1NDDQge8FegXwVN6Ren/u+X8dx6/TRgRIIXtbj2n2ieih1+SzTEccExtz3QgcXFx+ZxSM1O3Xoe5crrhltym4VHVynMHnup+h3TaL9tLmsoWiopb9GlEG1eTlXIoyPsKVt2FA+s1MJP5zVmQ=; akacd_www=2177452799~rv=53~id=9087b102caf120794dbb1eeceaf4ccc8; usn_session_id=891228875906785; usn_visitor_id=8912288759182043; optimizelyEndUserId=oeu1589122887855r0.7555247616511707; usprivacy=1YNY; s_cc=true; s_fid=6C0F54971BC55B63-31DB4C74AAF1424B; ntv_as_us_privacy=1YNY; _ga=GA1.2.1252831587.1589122893; _gid=GA1.2.1293277568.1589122893; _fbp=fb.1.1589122894850.768122457; _ntv_uid=a074b9dd-6b5b-4f4b-b257-f9e7ee116412; __gads=ID=3343601cd2e45d2f:T=1589122898:S=ALNI_MZI2Mh_V-ROYbHt3s2k1h83if7i8A; edu-page-views=2; modal-page-views=2; pageview-count-Best Colleges Q2 2020 Audience Survey=2; CUID=N,1589123026657:ALHGLuQAAAAPTiwxNTg5MTIzMDI2NjU3xMc3klevipXW6CRMhCp96C/0wAIB5hXG0/fOK/1Ol60Pak5Dv6v1GHuSJcnhwzLp/ZPAF0+w1p4ic6ZfQHqgJCnyVI1XNZdQ7uBtRQ7wisLYSy5p3bcKN45s8z0N5XX37CMtZHg8WMEvbF6Q+BNNPpjuqLZ3n2p0hJ8+nTpo1lq/vOQrVU+DCcsiC38OMawezCmWDdUxbg2PiMkU9F/WZ4MfddfaDwqQ1BBQC0QkUZeRHkOCPndfwQOCKX1IKZ81Ju7MTmN1wqFdHaHxmHICvLvD6er4q4B0o8byjDXO0M79Yt82UMi8E2sqIAzin+FaFk181KNB5Z+5LbvWhORCig==; FCCDCF=[["AKsRol8x0eLcCPRNK87LcFg96i4OohYRu7keT-wXifV77qo_eYe6uZ0ThI1Oxd2-Y4V5wtjFjZW02xgjl0IhpmE9ojyljTmH9lrVeqQI3wXUjtift1w_Dqsor4S-4hEwsOEhBLpQrx8Ijd3oIw7mqxKezHDHZiod4A=="],null,["[[],[],[],[],null,null,true]",1589123041768]]; education-compare-slideout-state=collapsed; s_sq=%5B%5BB%5D%5D; utag_main=v_id:0171ff1af36300170b586aee949903073006706b009dc$_sn:1$_ss:0$_pn:2%3Bexp-session$_st:1589125090368$ses_id:1589122888547%3Bexp-session$_prevpage:www.usnews.com%2Fbest-colleges%2Fsearch%3Bexp-1589126890272; kw.pv_session=6; sailthru_visitor=9abdf1e6-3e02-427f-9899-6c232865866f; bm_sv=C8E5F93ED4F69A94559E23D6F676C38F~k2zHi/YOvrX2jg2IjDjERaOLYsf7bu+NjQmXeUuPHueXWih3Xm6rjXIC8wg1E225YVqIN2Q3cxjMPj6wlfrOgX8K9b5WW9BLiQIddDKHAGX7gH591ibZ8/bJFn4E/h7PhohIoGJK8PpG6Vel3r3dp//PcCGwzvgJNlUWVUqki3c=; _sp_id.26f9=f626f911-80a4-4912-b0bc-ad1b520357f6.1589122896.2.1589128312.1589124442.54a5f830-9b4f-471e-b326-7e4654bf5bf1; _sp_ses.26f9=*; RT="sl=0&ss=1589123021504&tt=0&obo=0&bcn=%2F%2F684d0d40.akstat.io%2F&sh=&dm=usnews.com&si=a65156df-2f6b-4e2a-815d-f7fdf1e8928c`,
}
};
request(options, function (err, resp, html) {
debugger
if (!err) {
var res= JSON.parse(html);
//var items=res.data.items
//var totalItems=res.data.totalItems
//var totalPages=res.data.totalPages
}
})
Please try this code . maybe you have to put your browser cookie in the request url. since this site api is actually restricted for another applications. in the result

Categories