node.js URL dispatching without hardcoding - node.js

I need a corporate website with several pages
I've been able to produce that pages using below codes (using ECT template engine):
var http = require('http');
var url = require('url');
var ECT = require('ect');
var renderer = ECT({ root : __dirname + '/views' });
var data = { title : 'blabla' };
var front = renderer.render('front.ect', data);
var aboutus = renderer.render('aboutus.ect', data);
var ourtechnology = renderer.render('ourtechnology.ect', data);
var anypath = renderer.render('anypath.ect', data);
var server=http.createServer(function(req,res){
var pathname=url.parse(req.url).pathname;
switch(pathname){
case '/':
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(front);
break;
case '/aboutus':
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(aboutus);
break;
case '/ourtechnology':
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(ourtechnology);
break;
default:
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(anypath);
break;
}
}).listen(80);
console.log('Server running');
But above codes are hardcoded.
How to make URL dispatching without hardcoding?
(I need to be able to create the page just like posting blog post)
I prefer MySQL for the database, and need guide where to start

Given your need, you should use express, then the problem becomes:
var express = require('express');
var app = express();
app.get('/', callback_for_root_dir);
app.get('/aboutus', callback_for_aboutus);
app.get('/ourtechnology', callback_for_ourtechnology);
app.get('*', default_callback);
app.listen(80);
Using MySQL for routing would be awkward, because how are you going to define the corresponding callbacks with a new path? You still need to code the callback in Node.js and restart the app, so there is no much point using MySQL data for routing. Besides, you can use powerful regular expressions for paths with Express.

You can also use a library to extend express normal routing, and soft-code your routes even
more.
https://github.com/hrajchert/express-shared-routes

Related

Not able to use multiple html files in node js

I am not able to use more HTML files. I want to add 2-3 HTML links in my index.html, how can I do it? Below is my code where I used 2 html pagelinks, one css and one js.
This is test.js file:
var http = require('http'),
fs = require('fs');
http.createServer(function (req, res) {
if(req.url.indexOf('.html') != -1){ //req.url has the pathname, check if it conatins '.html'
fs.readFile(__dirname + '/public/index.html', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.js') != -1){ //req.url has the pathname, check if it conatins '.js'
fs.readFile(__dirname + '/public/js/bootstrap.min.js', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.css') != -1){ //req.url has the pathname, check if it conatins '.css'
fs.readFile(__dirname + '/public/css/bootstrap.min.css', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.html') != -1){ //req.url has the pathname, check if it conatins '.js'
fs.readFile(__dirname + '/public/hello.html', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
}
}).listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:3000/');
I got this error in cmd while loading server:
D:\Nodejs>node test.js
Server running at http://127.0.0.1:3000/
events.js:160
throw er; // Unhandled 'error' event
^
Error: write after end
at ServerResponse.OutgoingMessage.write (_http_outgoing.js:441:15)
at D:\Nodejs\test.js:45:13
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:446:3)
It seems that you are trying to write your own static file server in Node. There are easy solution for that, like the express.static middleware in the Express framework, where all you do is something like this:
Ready solution
var path = require('path');
var express = require('express');
var app = express();
var dir = path.join(__dirname, 'public');
app.use(express.static(dir));
app.listen(3000, () => console.log('Listening on http://localhost:3000/'));
(this is the entire program).
Reinventing the wheel
Now, if you insist of reimplementing all of that yourself then read on.
One of the bugs in your code is checking for the extensions when you in fact check for having strings like .html anywhere in the code. E.g. this:
if(req.url.indexOf('.html') != -1)
will match files like /my.html.styles/style.css which it shouldn't.
If you want to check the file extension then use:
path.extname(file);
See: https://nodejs.org/api/path.html
Also it doesn't terminate when it finds a match because you're using plain if and not else if blocks and you don't use early return in your code.
Another problem is that you have hardcoded all of the paths to files and with that code you will not be able to serve just any HTML or CSS code. Also your code will not serve images correctly, or multiple styles etc. It would actually be easier to rewrite from scratch that try to fix it.
Good examples
See this answer for examples of how to properly serve static images with Express andexpress.static, Express without express.static, connect, http module (like you do here) and net module with raw TCP connections:
How to serve an image using nodejs
Here is a full example of a file server using only http i.e. what you're trying to do here:
var path = require('path');
var http = require('http');
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
var server = http.createServer((req, res) => {
var reqpath = req.url.toString().split('?')[0];
if (req.method !== 'GET') {
res.statusCode = 501;
res.setHeader('Content-Type', 'text/plain');
return res.end('Method not implemented');
}
var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
res.statusCode = 403;
res.setHeader('Content-Type', 'text/plain');
return res.end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', () => {
res.setHeader('Content-Type', type);
s.pipe(res);
});
s.on('error', () => {
res.setHeader('Content-Type', 'text/plain');
res.statusCode = 404;
res.end('Not found');
});
});
server.listen(3000, () => console.log('Listening on http://localhost:3000/'));
This code is much more complicated and doesn't even serve properly files with other MIME types than the ones explicitly supported, which only demonstrates that it's much better to use module that does all of that properly.
So preferably use the right tool for the job, like express.static or any other well tested Node static file server and you will avoid a lot of the problems.
For more info see:
How to serve an image using nodejs
This is caused by your if statement. You should be using something like this:
if(req.url == '/index.html'){
// load index.html
}else if(req.url == '/hello.html'){
// load hello.html
}else if ... // the rest for css and js
Some explanations
When you check just the indexOf of the request url, you won't be able to differ index.html from hello.html (both have .html), so what you want to check is the file itself.
For that error, since any .html in the url matches both IFs, and you are not using if..else, it will enter both, calling end in the request in one IF, and trying to write after end in the other.
Side note
This should be used only for learning purposes. There is no need to use this method once you get a grasp on how it works and what's happening, because, when you do, just go with well-known tools that do the job in much simpler ways.

How do I combine two Express modules that both require listening to the port?

I am trying to create a web scraper where the user inputs a URL into a form and when they hit submit, the scraper gets the URL and then returns the data about the URL that I specifiy.
My main app.js file is:
// Dependencies
var express = require('express');
var path = require('path');
var fs = require('fs');
// Custom Libraries - ./ signals to node not to look in the node_modules directory
var scraper = require('./scraper');
// App.js Variables
var app = express();
var viewsPath = path.join(__dirname, '/app/views');
app.use(express.static(__dirname + '/app/public'));
// set the port - 3000
app.set('port', process.env.PORT || 3000);
// Form handling
app.use(require('body-parser').urlencoded({
extended:true }));
app.get('/the_test');
// Writes the domain entered in the form to app/data/domain.txt
app.post('/process', function(request, response){
var domain = request.body.domain;
fs.writeFile('app/data/domain.txt', domain, function (err) {
if (err) return console.log(err);
console.log('Your domain has been saved!');;
});
response.redirect(303, '/results');
});
// Routes require
var routes = require('./routes');
app.use('/', routes);
app.use('/results', routes);
app.listen(app.get('port'), function(){
console.log('Express started on http://localhost:' + app.get('port') + '; press Ctrl-C to terminate.');
});
My scraper file is:
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var scraper = express();
// Scrape the url that was posted
scraper.get('/scrape', function(req, res){
// Scrape this
var url = fs.readFileSync('./app/data/domain.txt', 'utf8');
request(url, function(error, response, html){
if(!error){
var $ = cheerio.load(html);
var header;
var json = { header : ""};
$('.hero-message').filter(function(){
var data = $(this);
header = data.children().first().text();
json.header = header;
});
} else {
console.log(error);
}
fs.writeFile('./app/data/results.json', JSON.stringify(json, null, 4), function(err){
console.log('File successfully written! - Check your project directory for the output.json file');
});
res.send('Check your console!')
});
});
scraper.listen(4000);
console.log('Magic happens on port 4000');
exports = module.exports = scraper;
When I go to localhost:3000, the user is able to enter the URL and hit submit, they are redirected to localhost:3000/results and the URL is logged in data/domain.txt.
When I go to localhost:4000/scrape, the scraper activates, grabs the domain from domain.txt and scrapes it.
My question is how can I make this one fluid program and/or how do I activate the scraper automatically instead of going to localhost:4000/scrape every time? I am very new to Node.js and Express and realize this is a lot of ugly code to look at.
Any tips would be greatly appreciated.
There's no need to keep two separate processes for what you are trying to do. What you can do is to move the scraper action
scraper.get("/scrape", function (req, res) {
// code
});
To the main app.js file and serve everything from port 3000, make sure you include all dependencies from scraper on main. At this point, you might want to learn how to use node's module system to keep code separated and organized.
Depending on how long your scrape process takes, you could do one of the following:
Change the process action to do the work that the scrape action does at the moment, so instead writing the domain to a file and then go to a different url to read from that file and start the process, you catch the domain and feed to the scraper right away.
If the scraper takes a long time and you want to start the scrape job automatically, you don't want it to block your application or to throw a timeout during the request. You should consider implementing a worker queue mechanism. There are a lot of ways to do this, and the correct solution depends a lot on the expected use case of your application.

Node JS: Where is my POST data?

I have no lack with sending POST request to node js server. I have a simple request and a simple server.
My server code is:
var http = require('http');
var bodyParser = require('body-parser');
http.createServer(function (req, res) {
console.log(req.body);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/')
my client request code is:
var val = JSON.stringify({ city:"SomeCity", name:"MyNameIsHere" });
alert(val);
$.ajax({
url: 'http://127.0.0.1:1337',
type: 'POST',
data: { value: val},
success: function(result) {
alert('the request was successfully sent to the server');}
});
So I suppose to get SomeCity and MyNameIsHere strings in the request body at the node js server, but the req.body field is undefined. Have to say that I open my test.html with request code locally with URL like this:
file:///D:/Projects/test.html
May be Im blind and overseen something trivial, but I have no idea what:)
Have to say that I open my test.html with request code locally with URL like this:
file:///D:/Projects/test.html
You're trying to post cross-domain, which you cannot do in this case. Serve your HTML over HTTP so that you can make a POST. If you use your browser's development tools, you will see that the request will never hit your Node.js server (except for a possible pre-flight request for CORS).
Another problem is that you're not actually using body-parser. If you want the post data, you will have to read from req like a stream.
You are including "body-parser" in var bodyParser = require('body-parser');, but you never actually use it. It won't magically parse the request for you. The default request object does not have a body property... see the documentation.
If you want to use the body-parser module, you should probably use express.js and read the documentation on how to connect it as middleware.
If you want to get the body using the built-in http server, you need to consume the request object first using something like this:
if (req.method == 'POST') {
var body = '';
req.on('data', function(data) {
body += data;
if (body.length > 1000000) {
req.connection.destroy();
}
});
req.on('end', function () {
console.log(req.body);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
});
}
Adding express library and bodyparser as middleware did the trick. However I could use the code from neelsg answer but working with built-in http server and handling Post data by my own is too burdensome.
So piece of my working code is here:
var express = require('express');
var http = require('http');
var url = require('url');
var bodyParser = require('body-parser');
var app = express();
app.use(express.bodyParser(
{
keepExtensions: true,
limit: 30*1024*1024 // lets handle big data
}
));
app.use(bodyParser.urlencoded());
Bodyparser by default can handle only 100Kb data in the post request, so I had to increase limit using its config options.

How to show the node.js output in HTMLPage

I am using webmatrix and building node.js application.
In that i want to pass the value from the node.js to the HTMLPage in the project.
var http = require('http');
var URL = require('url');
http.createServer(function (req, res)
{
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('Hi man');
}).listen(process.env.PORT || 8080);
With that code i tried it gives a page with hi man.Its obvious.But i want this to be rendered in the html page i am having in the project.
How to achieve tat.
Please give some suggestion on this..
First I recommend you use Expressjs, which is a framework for Nodejs.
Then I recommend you use EJS, an engine template for Nodejs.
When integreses all, you can use code similar to:
...
app.set('views', __dirname + '/views');
app.register('.html', require('ejs'));
app.set('view engine', 'html');
app.use(express.static(__dirname + '/public'));
app.use(express.favicon(__dirname + '/public/img/favicon.ico', { maxAge: 2592000000 }));
app.use(expressValidator);
...
And
...
app.get('/test', function(req, res){
res.render('test', {msg: "Hello World"});
});
...
Finally in your file views/test.html:
...
<div><%- msg %></div>
...
I hope it helps somewhat.
Additionally review:
http://utahjs.com/2010/09/25/nodejs-express-and-ejs-templates/
Render ejs file in node.js
Node.js - EJS example
Greetings.
Sounds like you want to request your nodejs server using AJAX and inject the server's response into an HTML element.
Check out: http://api.jquery.com/jQuery.getJSON/
Here's an example using jQuery to connect and retrieve the server response from node running on port 8080 of different host, and insert that into an HTML element.
node:
var http = require('http');
var url = require('url');
http.createServer(function (req, res)
{
//parse the url and query string from the request
var url_parts = url.parse(req.url, true);
//if the callback query parameter is set, we return the string (or object)
if(url_parts.query.callback){
var str = "Hi man";
res.writeHead(200, {'Content-Type':'text/html'});
res.end(url_parsed.query.callback+'("'+str+'")');
//if it's not set, let's return a 404 error
}else{
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404 Error');
}
}).listen(process.env.PORT || 8080);
index.html
<div id="my-div"></div>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function(){
//getJSON and the callback paramater make us cross-domain capable.
$.getJSON('http://myotherhost.com:8080/?callback=?', function(data){
$("#my-div").html(data);
});
});
</script>

Node.js, Express, Jade - Error: Can't set headers after they are sent

I'm getting the following error when trying to produce an HTML page using Jade. Has anybody else experienced this issue. I noted below where exactly the error is caused.
Error
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:644:11)
at ServerResponse.res.setHeader (/Users/dereklo/node_modules/express/node_modules/connect/lib/patch.js:59:22)
at ServerResponse.res.set.res.header (/Users/dereklo/node_modules/express/lib/response.js:475:10)
at ServerResponse.res.contentType.res.type (/Users/dereklo/node_modules/express/lib/response.js:350:15)
at ServerResponse.res.send (/Users/dereklo/node_modules/express/lib/response.js:111:14)
at res.render.fn (/Users/dereklo/node_modules/express/lib/response.js:672:10)
at Object.exports.render (/Users/dereklo/node_modules/jade/lib/jade.js:216:5)
Node.js/Express/Jade Source Code
var http = require('http'),
express = require('express'),
jade = require('jade'),
url = require('url'),
jsdom = require('jsdom'),
child_proc = require('child_process'),
w,
h,
scripts = ["/Users/dereklo/Node/pie/d3.min.js",
"/Users/dereklo/Node/pie/d3.v2.js",
"/Users/dereklo/Node/pie/d3.layout.min.js",
"/Users/dereklo/Node/pie/RadialScriptMobileServ.js",
"/Users/dereklo/Node/pie/RadialScriptMobile.js",
"/Users/dereklo/Node/pie/canvg.js"];
//scripts = ["./d3.v2.js",
// "./d3.layout.min.js",
// "./pie.js"]
htmlStub = '<!DOCTYPE html><div id="RadialScriptMobileServ"></div>',
querystring = require("querystring"),
fs = require("fs"),
formidable = require("formidable"),
path = require('path'),
request = require('request')
svgsrc = '';
//create an app server
var app = require('express').createServer();
//set path to the views (template) directory
app.set('views', __dirname + '/views');
//set path to static files
app.use(express.static(__dirname + '/../public'));
//handle GET requests on /
app.get('/', function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
w = (url.parse(req.url, true).query['width']);
h = (url.parse(req.url, true).query['height']);
console.log("width: ",w);
console.log("height: ",h);
request("http://dcaps-staging.media.mit.edu:8080/api/reality_analysis_service/get_reality_analysis_data?document_key=radialData&bearer_token=8e2f9e3129", function (err, result, json) {
json = JSON.parse(json);
console.log("my radial data: ",json.radialData.data);
jsdom.env({features:{QuerySelector:true}, html:htmlStub, scripts:scripts, done:function(errors, window) {
svgsrc = window.insertRadial("#RadialScriptMobileServ",w,h,json).innerHTML;
console.log(svgsrc);
res.render('/Users/dereklo/Node/pie/Jade_radial.jade', {pageTitle: 'Franz Enzenhofer'}); // ERROR OCCURRING HERE!
// res.write(svgsrc);
res.end();
}})
})
});
//listen on localhost:3000
app.listen(3000);
//console.log('Pie SVG server running at http://127.0.0.1:3000/');
My issue was that I needed to remove:
res.writeHead(200, {'Content-Type': 'text/plain'});
It now works perfectly. I hope this will help others who stumble upon this post...
You can do it two ways. Either add another route above your app.get(/ ..) like this:
// global controller
app.get('/*',function(req,res,next){
res.header('Content-Type': 'text/plain' , 0 );
next(); // http://expressjs.com/guide.html#passing-route control
});
Or you can add middle ware function to your existing route
addToHeader = function (req, res, next) {
console.log("add to header called ... " + req.url);
res.header('Content-Type': 'text/plain', '0');
next();
}
and then change your routes to sth like this:
app.get('/', addToHeader, function(req,res){
var stuff = { 'title': 'blah' };
res.render('mytemplate',stuff);
});
When using res.render(), you don't need to handle response manually (res.end(), res.writeHead(), etc.).

Resources