Express & NodeJS : Accessing response parameters from Multipart form POST - javascript

I have a simple form that allows the user to upload a file type image, and append the "category" type of the file to the response body, via a radio input selection.
The file processing itself is handled as expected on the backend - however, when it comes to accessing parameters in the response body, I am receiving UNDEFINED as the parameter value. Can anyone lend some pointers on what I may have overlooked ?
here is a sample of the mark-up and back end script:
<!DOCTYPE html>
<html>
<head>
<script src="js/blueimp-gallery.min.js" async></script>
<script src="js/bootstrap.min.js" async></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" async></script>
<script src="js/script.js" async></script>
<link rel="stylesheet" href="css/blueimp-gallery.min.css">
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="main">
<div class="navbar-wrapper" style="border: 12px solid black;">
<ul>
<li>Login</li>
<li>Sign up</li>
</ul>
</div>
<div>
<form
id = "uploadForm"
enctype = "multipart/form-data"
action = "/api/photo"
method = "post">
<input type="file" name="userPhoto" />
<input type="submit" value="Upload Image" name="submit">
<label>travel</label>
<input type="radio" value="travel" name="cat">
<label>food</label>
<input type="radio" value="food" name="cat">
<label>community</label>
<input type="radio" value="community" name="cat">
</form>
</div>
</body>
</html>
app.post('/api/photo', function(req,res){
console.log("request to upload image recvied.. upload in progress.");
//console.log("res.catype() = TODO " + req.get("cat"));
// Returning undefined*
console.log("res.catype() = " + res.cat);
// handle file persistence to disk.
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});

For multi part forms (forms with an attached file like an image), you need to use Multer (or some other multiform parser) on your NodeJS server instead of body-parser. The link will give you an example of how to set this up. Assuming your Client side HTML/JS is setup properly, you will be able to extract the image as a file and the request as body info.
The serverside NodeJS would look something like this:
app.post('/api/photo', upload.any(), function (req, res, next) {
var files = req.files;
if (files) {//Checks to see if any attached files
files.forEach(function (file) {
console.log('FILE');
console.log(JSON.stringify(file));//contents of file
console.log(JSON.stringify(file.filename));
});
}
console.log(req.body);
res.sendFile(__dirname + "/resources/JSONStructures/genericResponse.json");
});
If you could post your Client Side JavaScript it would help a little in determining what's causing the issue. Specifically I see a lot of AJAX being setup the short and easy way which will cause issues with multi forms.

You should access your cat value from req.body.cat, refer http://expressjs.com/en/4x/api.html#req.body for more detail

Not sure if you post your entire server-side code, but I don't any reference to the body-parser middleware. You should use it to process any incoming "post". And you should always declare them before your routes. This way, you can access the data with req.body.
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/api/photo', function(req,res){
console.log("request to upload image recvied.. upload in progress.");
//console.log("res.catype() = TODO " + req.get("cat"));
// Returning undefined*
console.log("res.catype() = " + res.cat);
// handle file persistence to disk.
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});

Related

POST 404 (Not Found) error while trying to write to http://localhost:3000/messages

I'm trying to build a basic website with client-server interaction via node js.
I have a near paradoxical issue where on one hand, going to http://localhost:3000/messages will show me the needed data
I know that there are other potential issues with the code. I will start working on them once I get past that odd 404 error
http://localhost:3000/messages
[{"name":"Jim","message":"heyo"},{"name":"Johny","message":"Yo"}]
but when trying to add more data to it via
$.post('http://localhost:3000/messages', messages)
while messages containing the data from two input fields (I was able to add data through those fields only on client side), pressing the button with the proper event listener yielded the following error in the console
POST http://localhost:3000/messages 404 (Not Found) jquery-3.5.1.min.js:2
(the jquery part is at the far-right side of the console frame).
The weirdest part is, that in the network tab of the dev window, two instances of http://localhost:3000/messages appear, while the second one marked as an error
1
2
here is the entire code, since I'm not sure what else might be important in my situation
HTML, CSS & JS, file name - index.html
<!doctype html>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<div class="container">
<br>
<div class="jumbotron">
<h1 class="display-4">Send Message</h1>
<input class="form-control" placeholder="name" id="name">
<input class="form-control" placeholder="message" id="message">
<button id="send" class="btn btn-success">Send</button>
</div>
<div id="messages"></div>
</div>
<script>
$('#send').click(()=>{
var messages = {
name: $('#name').val(),
message: $('#message').val()
}
hostMessage(messages)});
$(document).ready(()=>{
displayMessages();
console.log("The page has finished loading");
})
function addMessage(message){
$('#messages').append(`<h1>${message.name}</h1><h4>${message.message}`);
}
function displayMessages(){
$.get('http://localhost:3000/messages', (data)=>{
data.forEach(element=> {
addMessage(element)
}, this);
})
}
function hostMessage(messages){
$.post('http://localhost:3000/messages', messages)
}
</script>
NODE JS, serverside code, file name server.js
var bodyParser = require('body-parser');
var path = require('path');
var fs = require('fs');
var app = express();
app.use(express.static(__dirname));
app.use(bodyParser.json());
var messages = [
{name: "Jim", message: "heyo"},
{name: "Johny", message: "Yo"}
]
app.get('/messages', (req, res)=>{
res.send((messages))
})
app.post('messages', (req, res)=>{
messages.push(req.body)
console.log(req.body)
res.sendStatus(200)
})
var server = app.listen(3000,()=> {
console.log('Server is listening through port ', server.address().port);
});
Sorry if it's a bit messy, I'm still very much new to this whole thing.

Node how to manipulate HTML DOM - document not found error

I have a Node server that serves various different HTML pages. In one of the pages there is a form. When the form is clicked, an endpoint in the node server.js file consumes the form data. The same HTML page contains a element whose text content i'd like to modify when the form is submitted. I've seen various tutorials online showcasing how to use document.getElementById('predictionText').innerHTML = prediction; to set the value of the text dynamically using inline javascript. How do I achieve this using Node and external js?
Below is my code:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/styles.css">
</head>
<body class="bodyContent">
<p>This is the predictor page -- it will hold a form for the user to input relevant data to.</p>
<section class="active" id="navigationBarSection">
<ul>
<li>Home</li>
<li>Predictor</li>
<li>How it Works</li>
<li>About Us</li>
</ul>
</section>
<section id="predictorUserInputSection">
<form action="/post_data_to_predictor_algorithm" method="POST" id="attributeInputForm">
<input class="textInput" required name="averageAreaIncome" placeholder="Average Area Income" />
<input class="textInput" required name="averageAreaNumberOfRooms" placeholder="Average Area Number of Rooms" />
<input class="textInput" required name="averageAreaHouseAge" placeholder="Average Area House Age" />
<input class="textInput" required name="averageAreaNumberOfBedrooms" placeholder="Average Area Number of Bedrooms"/>
<input class="textInput" required name="areaPopulation" placeholder="Area Population"/>
<button id="submitButton">Submit</button>
</form>
</section>
<section id="predictionResultsSection">
<p id="predictionText"><font size="6">here </p>
</section>
<script src="server.js"></script>
</body>
</html>
The node server that should update the text:
//jshint esversion:8
//adding all required dependencies/packages
const express = require('express');
const path = require('path');
const fs = require("fs");
const bodyParser = require('body-parser'); //for parsing post requests
const request = require('request') //for making HTTP requests
//specifies that this app will be using express.
const app = express();
//middleware for processing POST requests a bit easier.
app.use(bodyParser.urlencoded({extended: false}));
//static AWS EC2 instance server port. Edit with caution.
const serverPort = 5001;
const FLASK_SERVER_LOCAL_ENDPOINT = "http://localhost:5000/predictPrice";
//Allow the use of static files in project directory
app.use('/js', express.static(__dirname + '/js'));
app.use('/html', express.static(__dirname + '/html'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/node_modules', express.static(__dirname + '/node_modules'));
app.use('/resources', express.static(__dirname + '/resources'));
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
//Handle all root requests.
app.get("/", function(req, res) {
res.sendFile(path.resolve("index.html"));
});
app.get("/index", function(req, res) {
res.sendFile(path.resolve("index.html"));
});
app.get("/predictor", function(req, res) {
res.sendFile(path.resolve("html/predictor.html"));
});
app.get("/how_it_works", function(req, res) {
res.sendFile(path.resolve("html/how_it_works.html"));
});
app.get("/about_us", function(req, res) {
res.sendFile(path.resolve("html/about_us.html"))
});
//HERE IS THE PROBLEM
app.post("/post_data_to_predictor_algorithm", (req, res) => {
//update prediction label in the UI:
console.log("Updating label!");
document.getElementById('predictionText').innerHTML = "received user response!";
});
When running the server, I get the following error when submitting the form:
ReferenceError: document is not defined
at app.post (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/server.js:90:3)
at Layer.handle [as handle_request] (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/layer.js:95:5)
at /Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/index.js:335:12)
at next (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/express/lib/router/index.js:275:10)
at /Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/body-parser/lib/read.js:130:5
at invokeCallback (/Users/vismarkjuarez/Documents/Github/RealEstatePriceEstimator/node_modules/raw-body/index.js:224:16)
if you want update DOM element in the same page of form, you have to build a dynamic form cause the standard one with action="/destination" will send you to a new page, or rebuild an existing one where you cant write on the document cause node dont know DOM. You need Jquery or work directly with xhr. For instance:
// Build a single field dynamic Form:
<div id="myForm">
<input type="text" id="textA"></input>
<button type="submit" onclick="getElements()"> Submit </button>
</div>
// The element that need be updated.
<section id="predictionResultsSection">
<p id="predictionText"><font size="6">here </p>
</section>
// Get the "form" field value.
function getElements() {
var tA = document.getElementById("textA");
formSubmit(tA.value)
}
// Do the request
function formSubmit(sendData) {
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "http://localhost/post_data_to_predictor_algorithm");
xhttp.send(sendData);
// Receive the response from server on this.responseText var
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
document.getElementById('predictionText').innerHTML = this.responseText;
}
};
}
// Server side
app.post("/post_data_to_predictor_algorithm", (req, res) => {
//do your logic and send the response
res.send(yourResponse) // the onreadystatechange will hadle this on responseText
});

Make mongoose request in express post?

I want to request my MongoDB database using a query obtained from a <form> in HTML inside a post, it's a bit tricky...
Here is my HTML :
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="interface.css" />
<meta charset="UTF-8" />
<title>Bienvenue sur MongoDB !</title>
</head>
<body>
<form method="post" action="/" id="formulaire">
<p>
<label for="rectangle">Nombre de rectangles maximum :</label>
<input type="range" min="0" max="10" id="rectangle" name="rectangle" />
<span id="texte1"></span>
</p>
<input type="submit" id="envoyer" value="Envoyer" />
</form>
</body>
</html>
Here is my Javascript :
var mongoose = require('mongoose');
var express = require('express');
var bodyParser = require('body-parser');
mongoose.connect('mongodb://localhost/image', function(err) {
if (err) {
throw err;
}
});
var app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.post('/', function (req, res) {
var query = Image.find(null);
query.lte('NbRectangles', req.body.rectangle)
query.exec(function (err, images) {
if (err) {
throw err;
}
for (var i = 0, l = images.length; i < l; i++)
console.log('Image : ' + images[i]);
});
});
app.listen(8080);
mongoose.connection.close();
This, displays nothing...
My MongoDB collection isn't empty I just removed some code, I did test and when I put the part inside the post, outside, It works but what I want is each time I click on the button It displays on the terminal the result of the request.
Thank you in advance...
It seems like when you receive a request your connection is closed. You should close your connection after executing your query. Also, you should check if your body is not empty and send the response after your request is processed: res.sendStatus(200).

NodeJS : How to have multiple methods for post

i am trying to make an iteractive form using nodejs.
Here is my web page
<!DOCTYPE html>
<html>
<script>
$('#ping-button').click(function() {
$.ajax({
type: 'POST',
url: 'http://localhost:3000/process_test'
});
});
</script>
<body>
<form action="http://127.0.0.1:3000/process_get" method="GET">
First Name: <input type="text" name="first_name"> <br>
Last Name: <input type="text" name="last_name">
<input type="submit" value="Submit">
</form>
<br>
<form action="http://127.0.0.1:3000/process_post" method="POST">
Team: <input type="text" name="team"> <br>
Player: <input type="text" name="player">
<input type="submit" value="Submit">
<br><br>
<button id='ping-button'>Ping</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</form>
</body>
</html>
and here is my app.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
// Create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.use(express.static('test'));
app.get('/index.html', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
})
app.get('/process_get', function (req, res) {
// Prepare output in JSON format
response = {
first_name:req.query.first_name,
last_name:req.query.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})
app.post('/process_test', urlencodedParser, function (req, res) {
console.log("Ping");
res.end(JSON.stringify("Ping"));
})
app.post('/process_post', urlencodedParser, function (req, res) {
// Prepare output in JSON format
response = {
team:req.body.team,
player:req.body.player
};
console.log(response);
res.end(JSON.stringify(response));
})
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
so i have two forms and a button. The first form is tied to the process_get method, the second form is tied to the process_post method and the button is tied to the process_test method.
both of the forms give expected results. When i click the button, it goes to the process_post method instead of the process_test
why does this happen?
The reason the Ping button submits the form to /process_post is because it is inside the form, and the click handler is not set due to errors.
If you open the console in your browser you will see Uncaught ReferenceError: $ is not defined. This is because you're using $ in your code, but you include jQuery much later in the page. This you can fix by loading jQuery prior to your script tag.
Once that is done you will notice that the button still does not work. This is because the script inside head is executed before the rest of the document is done, ie. the body contents is not there yet, and $('#ping_button') will find nothing. This you fix by wrapping you code inside $(document).ready(function() { // code here }); so it is executed once the whole page is loaded.
Still, clicking the button will send you to /process_post because the click event is not stopped in your click handler. Return false or use e.preventDefault() to disable the browser default action.
All this combined the head section (that was missing) will become
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#ping-button').click(function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: 'http://localhost:3000/process_test'
});
return false;
});
});
</script>
</head>
<body>
<!-- Rest of your page -->
</body>
</html>

Node.js: How would I use node-upload-progress to monitor file uploads while parsing additional form fields?

There are a couple of ways you could answer this. I'll gleefully accept a solution with node-upload-progress or an Express/formidable solution that allows for upload progress monitoring (I like both of those but Express's recent drop for formidable support is making my brain explode).
I'm attempting to create a page where files can be uploaded with progress monitoring, along with additional fields related to the file such as title, submitter name, file description etc.
The first partial solution I found was node-upload-progress. It worked great right away. Here's the example code pasted here for reference (I added an extra field I want to get the value for).
app.js
(function() {
var app = require('http');
var fs = require('fs');
var uploadProgress = require('node-upload-progress');
var uploadHandler = new uploadProgress.UploadHandler;
uploadHandler.configure(function() {
this.uploadDir = "" + __dirname + "/uploads";
this.onEnd = customOnEndHandler;
});
function customOnEndHandler(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Yay! Upload finished.');
}
app.createServer(function(req, res) {
if (req.url.match(/\/upload\?X\-Progress\-ID=.+/)) {
console.log('upload started');
return uploadHandler.upload(req, res);
} else if (req.url.match(/\/progress\?X\-Progress\-ID=.+/)) {
return uploadHandler.progress(req, res);
} else {
return fs.readFile("" + __dirname + "/index.html", function(err, data) {
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.write(data.toString());
return res.end();
});
}
}).listen(8080);
console.log('Server running at http://localhost:8080/');
}).call(this);
index.html:
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Upload</title>
<meta name="author" content="Pablo Cantero <pablo#pablocantero.com>">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<!-- Date: 2012-06-12 -->
<script>
$(function(){
var uploadIntervalID;
$('#form_upload').submit(function(){
// Preventing multiples clicks on upload
clearInterval(uploadIntervalID);
var xProgressID = guidGenerator();
$(this).attr('action', '/upload?X-Progress-ID=' + xProgressID);
uploadIntervalID = setInterval(function(){
$.get('/progress?X-Progress-ID=' + xProgressID, function(data){
if(data.status === 'done'){
clearInterval(uploadIntervalID);
}
updateUploadStatus(data);
}).error(function(){clearInterval(uploadIntervalID)});
}, 250);
return true;
});
function updateUploadStatus(data){
$('#upload_percent').text(data.percent);
$('#upload_status').text(data.status);
$('#upload_filename').text(data.fileName);
$('#upload_filepath').text(data.filePath);
}
// http://stackoverflow.com/a/105074/464685
function guidGenerator() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
});
</script>
</head>
<body>
<h1>Super Upload</h1>
<form action="/upload?X-Progress-ID=1" enctype="multipart/form-data" method="post" id="form_upload" target="iframe_upload">
<p>
<label>File</label><br/>
<input type="file" name="upload" id="upload"><br>
<span id="upload_percent"></span><br/>
<span id="upload_status"></span><br/>
<span id="upload_filename"></span><br/>
<span id="upload_filepath"></span>
</p>
<p>
<div>File Title</div>
<input name="file-title" type="text" maxlength="120" value="test value" />
</p>
<p>
<input type="submit" value="Upload">
</p>
</form>
<iframe id="iframe_upload" name="iframe_upload"></iframe>
</body>
</html>
When researching further about form parsing, I starting finding that formidable was the thing to use, and it worked great with Express (which was great for delivering the front-end I wanted to create and offered simpler basic authentication to access this)... until Express dropped/is dropping built-in formidable support.
Any good ideas and/or examples on how to work through this? My gut says to stick with node-upload-progress and figure out how to parse additional form values from scratch, so that I can better understand and appreciate the conveniences modules such as Express and formidable offer.
Thanks for helping a node.js new person in advance. :D

Categories

Resources