Node.js application to accept JSON from user, manipulate and save to MSSQL - node.js

I'm stuck. I thought my goal was going to be straight forward but I appear to be missing some key piece.
I'm trying to build a website that can accept a JSON file from an end user. Once the file has been provided I want to combine that data with data from a web-resource and then take the resulting flattened JSON and write it to a database table.
The first prototype of the project was built in a single HTML page with some JavaScript and I succeeded in getting the output the way I wanted it to look.
Then I tried to convert it to a node.js server so I could use an API to write the resulting JSON to the SQL server.
While the problem with which I am asking for help does not involve those steps I just wanted to share that there are some complexities of async that are possibly muddying things.
I've tried creating an HTML page that accepts a file and using express tried to capture that file, but I can't seem to figure out the right combination of middleware and other code to get the JSON file loaded into an object so I can feed it to my existing function to manipulate it and then subsequently feed it to a function to write it to SQL using a stored procedure.
So here's what I've got for my index.js
const express = require("express");
const app = express();
const fileUpload = require('express-fileupload');
const bodyParser = require("body-parser");
const path = require("path");
app.use(express.urlencoded({
extended: false
}));
app.use(express.json());
app.use(fileUpload());
app.post('/upload', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
let jsonFile = req.body.sampleFile;
console.log(jsonFile);
let parsed = JSON.parse(jsonFile);
console.log
res.send("blank page");
});
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/minimalIndex.html'));
});
const webserver = app.listen(5000, function() {
console.log('Express web server is running..');
});
and the HTML that it uses is this
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Static Index.html</title>
</head>
<body>
<form ref='uploadForm' id='uploadForm' action='/upload' method='post' encType="multipart/form-data">
<input type="file" name="sampleFile" />
<input type='submit' value='Upload!' />
</form>
</body>
</html>
The console.log shows undefined.
The JSON Parse fails because of an invalid JSON string.
I've succeeded at saving the file locally but that seems unnecessary.
Can someone please tell me what it is I am missing?

Everything is correct apart from the way you are trying to fetch the files. You are trying it from body while it should be from files. Something like this
let jsonFile = req.files.sampleFile;
let parsed = JSON.parse(jsonFile.data.toString("utf-8"));
Hope it helps

Related

How to return a 404 Not found page in an Express App?

I have an express app, in which I have the following code:
app.get('*', (req, res) => {
res.send('404', {
title: 404,
name: 'James Olaleye',
errorMessage: 'Page not found',
});
});
However, My IDE is warning about this message:
express deprecated res.send(status, body): Use
res.status(status).send(body) instead
And with the above code, My Browser is returning the following payload as a JSON object:
{
"title": 404,
"name": "James Olaleye",
"errorMessage": "Page not found"
}
What I want, is to display a 404 Not found page to the user, how can this be achived?
You have two seperate problem
1: you are using an old way to response to the request insted use this res.status(STATUS_CODE).send(BODY)
2: you are sending a json yet you want to display a 404 page in this case you need to send a html template
so your code should look like this
app.get('*', (req, res) => {
res.status(404).send("<div>404 Not Found</div>");
});
I updated your question a bit to make it clearer for future references.
the method res.send is deprecated, among other things because it's usages is too ambiguous. A server response, can be a lot of things, it can be a page, it can be a file, and it can be a simple JSON object (which you have here).
In your case, when you run res.send(404,{ /*...*/ }), the express app assumes you want to send a JSON object, so it does just that.
There are multiple possible ways, to achieve what you want, but I will stick to the most simple solution.
If you want to display an HTML page, in the most simplest form, you can actually just change your piece of code to do this instead:
app.status(404).send(`<h1>Page not found</h1>`)
This will essentially, show a page, instead of a JSON object.
You can even define the whole HTML file if you like:
app.status(404).send(
`
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
`
)
This would be the fastest way to achieve what you want.
A step further, would be to create an HTML file some where in your app, and to send the HTML file instead.
If your source code looks like this:
/
src/
index.js
htmls/
404.html
<!-- htmls/404.html -->
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
// src/index.js
const express = require('express');
const app = express();
const path = require('path');
const PORT = 3000;
app.get('/', function(req, res) {
const options = {
root: path.join(__dirname, '..', 'htmls')
};
res.sendFile('404.html', options, function (err) {
if (err) {
next(err);
} else {
console.log('Sent:', fileName);
}
});
});
This would allow you to have multiple HTML files which you can send around.
There are like I stated, other options as well, but that would make this answer way too long and out of scope. If you are interested, you can research Using template engines with Express and start with the following link.
Happy coding :)

How do I get HTML-linked JS files to use Require?

I am an express newbie, and I am trying to get a static HTML page to read details from a file in Express.
Initially I had a static HTML page where users could register where they were based on a particular day (as we are now rather flexible with working arrangements). However, it became apparent that I needed to store the current situation somewhere each time someone updated their details (and retrieve it when someone else signs on), so I did some investigating and came up with Node and Express.
I have an HTML file in the public directory, and it links to a JS file where I want to do a "fs" read.
app.js
app.use(express.static(path.join(__dirname, 'public')));
var steveRouter = require('./routes/steve');
app.use('/steve', steveRouter);
routes/steve.js
var express = require('express');
var path = require('path');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.sendFile(path.resolve(__dirname, '../public', 'steve.html'))
});
module.exports = router;
public/steve.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello World : title</title>
<script src="steve.js"></script>
</head>
<body onLoad="loadUp()">
<h3>small-ish heading</h3>
</body>
</html>
public/steveHtml.js
const fs = require('fs');
function loadUp() {
alert("Ciao");
let rawdata = fs.readFileSync('student.json');
let student = JSON.parse(rawdata);
console.log(student);
alert(student.name);
}
The server starts ok, but the browser shows the message "Uncaught ReferenceError: require is not defined at steveHtml.js:1"
This is probably the wrong way of doing it, but I would love to know :
how do I get the "called" JS file to recognise the require command?
is there a better/simpler/easier way of accomplishing what I want?

Upload Files using Express + Connect (Node)

I'm trying to upload files via a web site. As I'm new in web programming, I didn't found a full example in web that solves all my doubts.
My scenario is:
Nodejs (v.0.10.25) + Express (4.9.0)
Trying to use Connect (3.0).
I created my app using the Express command. So the app.js was created default. I figured out that Express doesn't have multipart by default. And I should install a middleware to use it.
I'm trying to install Connect. The question is: How do I configure it? Must I have to replace the Express server for Connect server or it can exist together ?
Some one can explain how does it works ? Or show an example ?
Thanks too much!
via http://howtonode.org/really-simple-file-uploads
<!-- client html -->
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file_upload">
</form>
// node.js server
var fs = require('fs'),
http = require('http');
app = (require('express'))();
app.post('upload', function(req, res){
fs.readFile(req.files.file_upload.path, function (err, data) {
var newPath = __dirname + "/uploads/uploadedFileName";
fs.writeFile(newPath, data, function (err) {
res.redirect("back");
});
});
});

Angular not updating html template variables when served via NodeJS server

This is a bit of a specific question, but I'm at a bit of a loss for an answer.
First, a little background. I've been trying to learn angular, and I wanted to start using Node as the backend. I currently have a working tutorial app that I can run locally that just returns data that is hard coded into the main controller.
When I moved the files to my NodeJS server, it stopped working though. Here is what works:
The files load correctly - there are no console errors, and I can view each of the files in the source (index.html, app.js, maincontroller.js)
The scope exists, and the variables are defined. I put a console.log($scope) inside the mainController.js file, and I can see all of the variables defined correctly.
Non-angular javascript works - I can place alerts outside/inside the mainController, and they all work correctly (also, console.log obviously works)
I am serving the files via a simple Node.js server. I am using express and hbs. I was originally using compression, and 0 cache length, but have since removed those with no change in the result.
The specific issue I'm having is that none of the template variables update. I've simplified it down to the following code for testing. When viewed locally, the page says 'I now understand how the scope works!', when served from Cloud 9, the structure exists, but the {{understand}} variable in the template doesn't work.
index.html
<!DOCTYPE html>
<head>
<title>Learning AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/maincontroller.js"></script>
</head>
<body>
<div id="content" ng-app="MyTutorialApp" ng-controller="MainController">
{{understand}}
</div>
</body>
app.js
var app = angular.module('MyTutorialApp',[]);
maincontroller.js
app.controller("MainController", function($scope){
$scope.understand = "I now understand how the scope works!";
});
server.js (Node server on Cloud 9)
var express = require('express');
var app = express();
var hbs = require('hbs');
app.set('view engine','html');
app.engine('html',hbs.__express);
app.configure(function() {
app.set('views', __dirname);
});
//app.use(express.compress());
app.use('/js',express.static(__dirname + '/client/js'));
app.use('/css',express.static(__dirname + '/client/css'));
app.use('/img',express.static(__dirname + '/client/img'));
//router
app.get('/',function(req,res){
res.render('client/index.html');
return;
});
//404 responses
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('client/404.html', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
});
app.listen(process.env.PORT);
console.log('listening on port '+process.env.PORT);
everythin became clear when i read
"Handlebars.js is an extension to the Mustache templating language"
what this menas is that hbs uses {{}} as delimiters as well as angular so the {{understand}} in your html never gets to angular because is first parsed and substituted by hbs. if you want to use hbs with angular youll need to change your delimiters using your angulars $interpolateProvider in your app configuration something like
$interpolateProvider.startSymbol('{/{');
$interpolateProvider.endSymbol('}/}');
You can use \{{understand}} as this will counter your hbs and put your angular on top.

CSRF token not working when submitting form in express

i'm trying to get forms working in my express app. i have a middleware function that passes the csrf token, req.session._csrf, to res.locals.csrf_token, so the view can use it. now i'm trying to use the local variable in my view and i'm getting a forbidden error from my session middleware.
here's my form code - i'm using handlebars as my templating engine:
<form method='post' action='/api/entries' enctype='multipart/form-data' >
<input type='hidden' name='_csrf' value={{csrf_token}} />
<input class='foo' type='text' />
<input class='bar' type='text' />
<button id='submit' type='submit'> SUBMIT
</form>
i've tried referencing the csrf_token variable with and without the double curly braces and neither works. any ideas on what i am doing wrong? the Error: Forbidden happens before my route function for POSTing to /api/entries is even called. so i'm pretty sure the problem is that i'm doing something wrong with referencing the csrf token..
*edit:*in regards to the "req.session._csrf is deprecated, use req.csrfToken() instead" getting logged to the console, i did:
grep -r '_csrf' .
in my app directory. here was the output.. it doesn't look like i'm referencing it anywhere besides the view, where my hidden CSRF field is named "_csrf"..
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: var secret = req.session._csrfSecret;
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: req.session._csrfSecret = secret;
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: Object.defineProperty(req.session, '_csrf', {
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: console.warn('req.session._csrf is deprecated, use req.csrfToken() instead');
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: return (req.body && req.body._csrf)
./node_modules/express/node_modules/connect/lib/middleware/csrf.js: || (req.query && req.query._csrf)
./v/home.hbs: <input type='hidden' name='_csrf' value={{csrf_token}} />
./v/show.hbs: <input type='hidden' name='_csrf' value={{csrf_token}} />
here is the entire error stack i'm getting when trying to POST to the /api/entries endpoint (i stupidly neglected to mention this before, but i'm using connect-redis for session middleware):
Error: Forbidden
at Object.exports.error (appFolder/node_modules/express/node_modules/connect/lib/utils.js:63:13)
at createToken (appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:82:55)
at Object.handle (appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:48:24)
at next (appFolder/node_modules/express/node_modules/connect/lib/proto.js:193:15)
at next (appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:318:9)
at appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:342:9
at appFolder/node_modules/connect-redis/lib/connect-redis.js:101:14
at try_callback (appFolder/node_modules/redis/index.js:580:9)
at RedisClient.return_reply (appFolder/node_modules/redis/index.js:670:13)
at ReplyParser.<anonymous> (appFolder/node_modules/redis/index.js:312:14)
edit 2: the error in connect-redis.js is a function trying to get the current session by the session ID and failing. don't know why this would be happening, my connect-redis setup looks correct. this is killing me
EDIT: If you don't need file uploads, don't use the multipart/form-data enctype. Switching to the default enctype would allow express.csrf() to parse the _csrf token.
In order to parse forms with the multipart/form-data enctype, you need use a multipart parser in your app configuration, or handle file uploads yourself. It's recommended to avoid using the included express.bodyParser() and instead use something like busboy or formidable on the routes you're expecting file uploads, to prevent an exploit.
If you go this route, your _csrf field will no longer be caught by express.csrf() because the form body will not be parsed until after the request passes that middleware. Set your form action to '/api/entries?_csrf={{csrf_token}}' to get around this.
var fs = require('fs');
var async = require('async');
var express = require('express');
var formidable = require('formidable');
var app = express();
app.use(express.urlencoded())
.use(express.json())
.use(express.cookieParser())
.use(express.session())
.use(express.csrf())
app.get('/upload', function(req, res) {
// File uploads ignored.
res.render('upload', {_csrf:req.csrfToken()});
});
app.post('/upload', function(req, res) {
// Explicitly handle uploads
var form = new formidable.IncomingForm();
form.uploadDir = 'temp';
var count = 0;
var maxAllowed = 10;
form.onPart = function(part) {
if (!part.filename) return form.handlePart(part);
count++;
// Ignore any more files.
if (count > maxAllowed) return part.resume();
form.handlePart(part);
};
form.parse(req, function(err, fields, files) {
// Process the files. If you don't need them, delete them.
// Note that you should still reap your temp directory on occasion.
async.map(Object.keys(files), function(key, cb) {
fs.unlink(files[key].path, cb);
}, function(err) {
res.end();
});
});
});
CSRF syntax has changed slightly in the latest versions of Express/Connect. You now want your middleware to look like this:
.use(express.csrf())
.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
res.locals.csrftoken = req.csrfToken();
next();
})
For testing your code, note that you first need to GET the form page in order to generate the CSRF token. Only then will your POST succeed. If it fails, you need to reload the page in the browser before trying to POST again.
I too hit this problem today and it's taken several hours for me to find a solution. Hopefully this answer helps someone with my exact problem. As #amagumori, I'm using redis for session handling and express 3.4.8, connect-redis 1.4.7.
Basically I was able to determine that the order of my express configuration affects the number of times a new token was issued. It seemed like everything being served out of public was creating a new token.
Specifically in my case I had to move the calls
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(express.static(__dirname + '/public'));
above
app.use(express.csrf());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
next();
});
and tokens are issued as expected for sessions.

Resources