In my function "reqHandler" I collect form data and pass it into my http.request. To chain requests, I declared a Promise and .then handler. Problem is that:
1. This is written into console "Unhandled promise rejection (rejection id: 2): TypeError: Cannot read property 'url' of undefined"
2. It seems like .then is not invoked, so no API calls are made.
Code:
"use strict";
const http = require("http");
const qs = require("querystring");
const fs = require("fs");
const PORT = 3000;
let resObject = {};
let hash = "";
const options = {
hostname: "netology.tomilomark.ru",
path: "/api/v1/hash",
method: "post"
};
const reqHandler = (req, res) => {
return new Promise((resolve, reject) => {
if (req.url === "/") {
switch (req.method.toLowerCase()) {
case "get":
// Browse my form with "Name" and "Surname" inputs
fs.readFile("./logs/form.html", (err, file) => {
if (err) {
reject("Promise rejected");
return
}
res.writeHead(200, {'Content-Type': 'text/html','Content-Length':file.length});
res.write(file);
res.end();
});
break;
case "post":
// Collect form data and parse it using querystring
let body = "";
req.setEncoding("utf8");
req.on("data", (data) => {
body += data;
if (body.length > 1e6)
req.connection.destroy();
});
req.on("end", () => {
let post = qs.parse(body);
console.log(post);
options.headers = {
"Content-Type": "application/json",
"firstname": `${post.firstName}`
};
options.body = {
"lastname": `${post.lastName}`
};
// Resolve with "options" object that has headers and body
resolve(options);
});
break;
default:
badRequest(res);
}
} else {
notFound(res);
}
});
};
reqHandler()
.then((options) => {
http.request(options, (res) => {
let resString = "";
res.on("data", (data) => {
resString += data;
});
res.on("end", () => {
console.log(resString);
});
res.end();
});
})
.catch(err => {throw err});
let badRequest = (res) => {
res.statusCode = 400;
res.setHeader('Content-Type', 'text/plain');
res.end('Bad Request');
};
let notFound = (res) => {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found');
};
const server = http.createServer();
server.on("error", (err) => console.error(err));
server.on("request", reqHandler);
server.on("listening", () => console.log(`Start HTTP on port ${PORT}`));
server.listen(PORT);
Ultimately, what´s wrong with my promise and .then? Any help will be appreciated!
Ok I have create a test environment and here you have an abstract version of your source.
Your mistakes was to wrap your promise in a function which pass the req and res parameters and you have to call resolve or reject inside your promise, what have been forgotten on several places.
This source is tested!
const http = require('http');
const reqHandler = (req, res) => {
return new Promise((resolve, reject) => {
if (req.url === "/") {
switch (req.method.toLowerCase()) {
case "get":
console.log('get');
return resolve('get');
case "post":
console.log('post');
return resolve('post');
default:
return resolve('default');
}
} else {
return resolve('some thing else');
}
});
};
const myReqHandler = (req, res) => {
reqHandler(req, res).then(()=> {
console.log('then reached')
});
}
const PORT = 8089;
const server = http.createServer();
server.on("error", (err) => console.error(err));
server.on("request", myReqHandler);
server.on("listening", () => console.log(`Start HTTP on port ${PORT}`));
server.listen(PORT);
Related
I have simple app.js for Node.js under localhost:3000
app.js:
let http = require('http');
http.createServer((req, res) => {
res.writeHead(200);
let response;
if(~req.url.indexOf('post')) {
response = req.body.content;
} else {
response = '<script src="http://localhost/fetch.js"></script>';
}
res.end(response);
}).listen(3000);
The file fetch.js is placed on my another local server and is successfully enqueued to the page
fetch.js:
read('http://localhost:3000/?post').then((response) => {
console.log(response);
});
async function read(url) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify({
content: 'Text'
})
});
return response.text();
}
So I render HTML with fetch.js which then send POST request to the same page, but with a query ?post
However, when I run node app.js I get the error
Can not read property 'content' of undefined
So I don't get req.body
Why and how to resolve?
i think you are missing parser for your http server, there is no body because you actually didn't parse the body.
assemble the chunks like below then parse it as the header sais.
this is my work for myself
private parseBody(req: IncomingMessage) {
return new Promise((resolve, reject) => {
const chunks: any[] = []
req.on("data", (chunk) => {
chunks.push(chunk)
})
req.on("end", () => {
const data = Buffer.concat(chunks)
switch (req.headers["content-type"]) {
case "application/json":
resolve(this.parseJson(data.toString()))
break
case "application/x-www-form-urlencoded":
resolve(this.parseUrlEncoded(data.toString()))
break
default:
resolve({})
}
})
})
http server is very abstract and doesn't support anything basicly, i suggest using express or fastify.
working example: https://frontendguruji.com/blog/how-to-parse-post-request-in-node-js-without-expressjs-body-parser/
update
this is the class im using
http.resolver.ts
private parseJson(data: string) {
return JSON.parse(data)
}
private parseUrlEncoded(data: string) {
const parsedData = new URLSearchParams(data)
const dataObj: any = {}
for (var pair of parsedData.entries()) {
dataObj[pair[0]] = pair[1]
}
return dataObj
}
private parseBody(req: IncomingMessage) {
return new Promise((resolve, reject) => {
const chunks: any[] = []
req.on("data", (chunk) => {
chunks.push(chunk)
})
req.on("end", () => {
const data = Buffer.concat(chunks)
switch (req.headers["content-type"]) {
case "application/json":
resolve(this.parseJson(data.toString()))
break
case "application/x-www-form-urlencoded":
resolve(this.parseUrlEncoded(data.toString()))
break
default:
resolve(parse(req.url ?? "/", true).query)
}
})
})
}
you may use await behind the parseBody function after
Ok, thanks to #Fide. The link he posted has the answer:
let http = require('http');
http.createServer((req, res) => {
if(~req.url.indexOf('post')) {
let body;
req.on('data', function(data) {
body = data;
})
req.on('end', function() {
res.writeHead(200);
res.end(body);
})
} else {
res.writeHead(200);
res.end('<script src="http://localhost/fetch.js"></script>');
}
}).listen(3000);
I've been reading about async and await in JS and tried to implement them in my code (which I totally messed up).
Here is my JS.
var express = require('express');
var router = express.Router();
var jsforce = require('jsforce');
const SEC_TOKEN = 'SEC_TOKEN';
const USER_ID = 'USER_ID';
const PASSWORD = 'PWD';
const { default: axios } = require('axios');
router.get("/", async (req, res, next) => {
await initConnect;
await soqlData;
await slackPostTest;
});
initConnect = async () => {
var conn = new jsforce.Connection({
loginUrl: 'https://login.salesforce.com'
});
await conn.login(USER_ID, PASSWORD + SEC_TOKEN, (err, userInfo) => {
if (err)
console.log(err);
else {
console.log(userInfo.Id);
}
});
}
soqlData = async () => {
await conn.query('Select Id, Name from Account LIMIT 1', (err, data) => {
if (err)
console.log(err);
else
return data.records[0];
})
}
slackPostTest = async () => {
await axios.post('SLACK_WEBHOOK', {
"text": "soqlRes"
})
}
module.exports = router;
What I am trying to achieve?
Initialize my connection by passing in SEC_TOKEN, USER_ID, PASSWORD to my initConnect function this will give me a connection (conn).
Use this conn and query my salesforce instance and get some other data.
post some message(currently irrelevant, but will hook up with the above response later) to my slack endpoint.
Also can someone please give me a little detailed explanation of the solution (in terms of async/await)?
Thanks
Assuming everything else about your JsForce API usage was correct (I have no experience with it, so I can't say), here's how to promisify those callback-based APIs and call them.
var express = require("express");
var router = express.Router();
var jsforce = require("jsforce");
const SEC_TOKEN = "SEC_TOKEN";
const USER_ID = "USER_ID";
const PASSWORD = "PWD";
const { default: axios } = require("axios");
router.get("/", async (req, res, next) => {
const { conn, userInfo } = await initConnect();
const data = await soqlData(
conn,
"Select Id, Name from Account LIMIT 1",
);
await slackPostTest(data.records[0]);
});
function initConnect() {
const conn = new jsforce.Connection({
loginUrl: "https://login.salesforce.com",
});
return new Promise((resolve, reject) => {
conn.login(USER_ID, PASSWORD + SEC_TOKEN, (err, userInfo) => {
if (err) return reject(err);
resolve({ conn, userInfo });
});
});
}
function soqlData(conn, query) {
return new Promise((resolve, reject) => {
conn.query(query, (err, data) => {
if (err) return reject(err);
resolve(data);
});
});
}
function slackPostTest(soqlRes) {
return axios.post("SLACK_WEBHOOK", {
text: soqlRes,
});
}
module.exports = router;
** I am trying to fetch the data submitted by POST method but I am not able to fetch the data because body variable is not getting updated inside req.on("data", data=> {}).**
const {createServer} = require("http");
const {createReadStream} = require("fs");
const {decode} = require("querystring");
const sendfile = (res, status, type, filepath) => {
res.writeHead(status, {"Content-Type" : type});
createReadStream(filepath).pipe(res); //readable stream work with writable stream
};
createServer((req, res) =>{
if(req.method === 'POST') {
let body = "";
req.on("data", data => {
body+=data;
});
req.on("end", () => {
const {name, email, message} = decode(body);
console.log(`Name : ${name}`);
console.log(`Email : ${email}`);
console.log(`Message : ${message}`);
});
}
switch(req.url) {
case "/" : return sendfile(res, 200, "text/html", "./home_page.html");
case "/message" : return sendfile(res, 200, "text/html", "./forms.html")
default : return sendfile(res, 200, "text/html", "./404.html");
}
}).listen(8000);
should be:
req.on("data", (data)=> {})
I am working an Express web application that runs JavaScript scraping code whenever the page is first loaded.
Here is the node web scraping code (scrape.js):
const request = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');
const data = require('../public/state_data.json');
const cases_data = require('../public/cases_data.json');
// retrieve wikipeida page
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html) => {
if(!error && response.statusCode == 200) {
// create cheerio scraper
const $ = cheerio.load(html);
// find, and loop through all the rows in the table
var rows = $('.wikitable').find('tr');
for(var i = 3; i < 59; i++) {
// scrape state name and cases from table
var state = $(rows[i]).children('th:nth-child(2)').text().split("\n");
var cases = parseInt($(rows[i]).children('td').html().replace(",", ""));
// update state_data.json file w/ proper cases and per capita
for(var j = 0; j < data.length; j++) {
if(data[j].state === state[0]) {
// push new data to cases_data.json
cases_data.push({
state: state[0],
latitude: data[j].latitude,
longitude: data[j].longitude,
cases: cases,
percapita: (cases / data[j].population)
});
// write to new cases_data.json file w/ state name, cases and calculated per capita
fs.writeFile('../public/cases_data.json', JSON.stringify(cases_data, null, 2), function(err) {
if (err) throw err;
});
}
}
}
} else {
console.log('request error')
}
});
And here is the express app (app.js):
const express = require('express');
const app = express();
const port = 3000;
const scrape = require('./scrape.js');
app.get('/', (req, res) => {
scrape();
res.render('index');
})
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
Right now when I run 'node app.js' I get this as an error:
TypeError: scrape is not a function
I've tried wrapping scrape.js in a function to no avail. Any ideas?
Fix / Solution:
I had to export the request function as shown in code below:
module.exports = () => {
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html)) => {
... remaining code ...
}
}
You need to export a function from scrape.js, Like:
const request = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');
const data = require('../public/state_data.json');
const cases_data = require('../public/cases_data.json');
module.export = () => {
// retrieve wikipeida page
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html) => {
...
});
};
Also, I suggest you to handle your async code with a callback or a promise, like:
With promise:
// scrape.js
module.export = () => {
return new Promise((resolve, reject) => {
// retrieve wikipeida page
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html) => {
if(!error && response.statusCode == 200) {
...
resolve();
} else {
console.log('request error');
reject(error);
}
});
});
};
// app.js
app.get('/', (req, res) => {
scrape()
.then(() => {
res.render('index');
})
.catch((error) => {
// response with an error
});
});
With callback:
// scrape.js
module.export = (cb) => {
// retrieve wikipeida page
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html) => {
if(!error && response.statusCode == 200) {
...
cb(null);
} else {
console.log('request error');
cb(error);
}
});
};
// app.js
app.get('/', (req, res) => {
scrape((error) => {
if (error) {
// response with an error;
return;
}
res.render('index');
});
})
Export the function:
// scrape.js
module.exports = () => {
request('https://en.wikipedia.org/wiki/2020_coronavirus_pandemic_in_the_United_States', (error, response, html) => {
// rest of function
}
}
So we are supposed to set up an WebServer with Fuseki and Nodejs.
I defined the classes with the following OWL Ontology and loaded it up to the Fuseki server.
<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:ns0="http://localhost:3000/data#">
<owl:DatatypeProperty rdf:about="http://localhost:3000/data#message">
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://localhost:3000/data#firstname">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://localhost:3000/data#lastname">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
</owl:DatatypeProperty>
<owl:Class rdf:about="http://localhost:3000/data#linkeddata">
<rdfs:comment>The class of all data types.</rdfs:comment>
<rdfs:label>The data type</rdfs:label>
</owl:Class>
<owl:Class rdf:about="http://localhost:3000/data#users">
<rdfs:comment>Users that post Tweets.</rdfs:comment>
<rdfs:label>Users</rdfs:label>
<rdfs:subClassOf rdf:resource="http://localhost:3000/data#linkeddata"/>
</owl:Class>
<owl:Class rdf:about="http://localhost:3000/data#tweets">
<rdfs:comment>Tweets contain messages of Users.</rdfs:comment>
<rdfs:label>Tweets</rdfs:label>
<rdfs:subClassOf rdf:resource="http://localhost:3000/data#linkeddata"/>
</owl:Class>
<owl:ObjectProperty rdf:about="http://localhost:3000/data#tweetedby">
</owl:ObjectProperty>
<rdf:Description rdf:about="http://localhost:3000/data#1234">
<rdf:type rdf:resource="http://localhost:3000/data#tweets"/>
<ns0:message>blablubbtweet</ns0:message>
<ns0:tweetedby>
<ns0:users rdf:about="http://localhost:3000/data#2345">
<ns0:firstname>Manuel</ns0:firstname>
<ns0:lastname>Neuer</ns0:lastname>
</ns0:users>
</ns0:tweetedby>
</rdf:Description>
</rdf:RDF>
I hope you can read it. It works however. I can then view the classes directly by typing this into Fuseki web interface. With "'2345" for example being the User Manuel Neuer.
SELECT * WHERE { GRAPH ?g { <http://localhost:3000/data#2345> ?p ?o}}
However, in my Node.js application, I somehow am only able to get the whole graph, and I dont know why.
The following is the Nodejs code :
I think the error must be at
function sparql_GET_Data_Element (URIhash) {
postData =
` describe <http://localhost:3030/data#` + URIhash + `>`
Its like he would always describe http:/localhost:3030/data# and never take the urihash. I would be delighted about every tip. Thanks alot. I test it by sending GET requests with Insomnia Rest Api.
http://localhost:3030/data#2345 this get request generates the same whole graph as http://localhost:3030/data#users.
const express = require('express')
const app = express()
const app_port = 3000
const apache_fuseki_persistence_url = 'http://localhost:3030'
const http = require('http');
const rdf_url_prefix_tag = 'PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>'
const rdff_url_prefix_tag = 'PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>'
const SPARQL_GET_ALL_CLASSES = function (){
postData =
`
prefix owl: <http://www.w3.org/2002/07/owl#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT DISTINCT ?class ?label ?description
WHERE {
?class a owl:Class.
OPTIONAL { ?class rdfs:label ?label}
OPTIONAL { ?class rdfs:comment ?description}
}
`
const options = {
hostname: 'localhost',
port: 3030,
path: '/data/query',
method: 'POST',
headers: {
'Accept': 'application/sparql-results+json',
'Content-Type': 'application/sparql-query'
}
}
return new Promise(function(resolve, reject) {
var req = http.request( options, function(res) {
// reject on bad status
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var data = {};
res.on('data', function(chunk) {
data = chunk;
});
// resolve on end
res.on('end', function() {
resolve(JSON.parse(data));
});
});
// reject on request error
req.on('error', function(err) {
// This is not a "Second reject", just a different sort of failure
reject(err);
});
if (postData) {
req.write(postData);
}
// IMPORTANT
req.end();
});
}
function sparql_GET_Data_Element (URIhash) {
postData =
` describe <http://localhost:3030/data#` + URIhash + `>`
const options = {
hostname: 'localhost',
port: 3030,
path: '/data/query',
method: 'POST',
headers: {
'Accept': 'application/ld+json',
'Content-Type': 'application/sparql-query'
}
}
return new Promise(function(resolve, reject) {
var req = http.request( options, function(res) {
// reject on bad status
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error('statusCode=' + res.statusCode));
}
var data = [];
res.on('data', function(chunk) {
data.push(chunk);
});
// resolve on end
res.on('end', function() {
resolve(JSON.parse(data[0]));
});
});
// reject on request error
req.on('error', function(err) {
// This is not a "Second reject", just a different sort of failure
reject(err);
});
if (postData) {
req.write(postData);
}
// IMPORTANT
req.end();
});
}
function sparql_CREATE_NEW_CLASS(new_rdfs_class){
}
// var promiseB = promiseA.then(function(result) {
// // do something with result
// });
function rdfs_class_exists (requested_class){
return SPARQL_GET_ALL_CLASSES().then(result => {
let found_class = false;
result.results.bindings.forEach(element => {
if (element.class.value == 'http://localhost:3000/data#'+requested_class) {
found_class = true;
}
})
return found_class;
});
}
app.get('*', function(req, res, next){
path_elements = req.path.split('#');
var requested_class = path_elements[1];
rdfs_class_exists(requested_class).then((RDFS_Class_found) => {
if (RDFS_Class_found){
if(path_elements.length > 3){
sparql_GET_Data_Element(path_elements[3]).then(result => {
console.log('result: ' + result);
res.send(result);
}).catch(error => {
console.log(error);
res.send(error);
})
} else {
sparql_GET_Data_Element(path_elements[1]).then(result => {
console.log('result: ' + result);
var ele = sparql_GET_Data_element(path_elements[1]);
res.send(ele);
// res.send(result);
}).catch(error => {
console.log(error);
res.send(error);
})
}
} else res.status(400).send("RDFS Class not Found");
});
})
app.post('*', function(req, res, next){
next();
})
app.delete('*', function(req, res, next){
next();
})
app.put('*', function(req, res, next){
next();
})
app.all('*', function (req, res, next){
res.status(400).send('No Elements found');
})
/**
* Die API wartet auf Requests am Port 3000.
* Wird sie per Node gitlokal gestartet läuft sie unter http://localhost:3000/
*/
app.listen(app_port, function () {
console.log('Example app listening on port 3000!');
});