Setup file-system based routing in Node - javascript

I'm really a fan of the simplicity PHP offers in serving pages, everything is based on the filesystem. I want to do the same thing with Node. I tried one routing setup that worked like this for views, but broke my public folder:
//using express:
app.get('*', function(req, res) {
file = req.params[0].substr(1, req.params[0].length);
console.log('requesting: ' + file);
res.render(file, {locals: {
req: req,
params: req.query
}});
});
So...What's the best way to setup filesystem based/php style routing in Node?

I think that I build exactly what you are looking for. I use this to serve up .jade files, obviously you can tweak this for your use case.
var url = require('url');
var express = require('express');
var app = express.createServer();
var fs = require('fs');
app.set("view engine", "jade");
app.use(app.router);
app.use(express.static(__dirname + '/public'));
/**
* Generic "get" attempts to route to known JADE files.
* If no known JADE files, then we pass routing to next() (should be static).
*/
app.get('*', function(req, res, next) {
var pathname = url.parse(req.url).pathname.toLowerCase(); // make matching case insenstive
// First case: with no path name, render the default index.jade
if(!pathname) {
res.render('index', {});
}
// Second case: path ending in '/' points to a folder, use index.jade from that folder
else if (pathname === '/' || pathname.charAt(pathname.length-1) === '/' ){
res.render(__dirname + '/views' + pathname + 'index.jade', {});
}
// Third case: looks like an actual file, attempt to render
else {
// Attempt to find the referenced jade file and render that. Note 'views' is default path.
fs.stat( (__dirname + "/views" + pathname + '.jade'), function(err, stats){
// There was an error, the file does not exist pass control to the static handler
if(err || !stats) {
next();
}
// We found the file, render it.
else{
res.render(pathname.substring(1), {});
}
});
}
});
app.listen(port);
Note, there should be more app.use() statements there for handling cookies, parsing the body etc. Also, the second param of the render is always empty. You may want to fill this out with things like {layout: xyz} or generic variables that need to come in to the rendered pages.

You can use express.static()
For examples:
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
app.configure(function(){
app.use('/uploads', express.static(PATH_TO_UPLOAD_FOLDER));
});

Related

Do changes to request.url in Express middleware persist on to the next middleware?

I have a middleware that rewrites the request.url. However in the next() middleware, the request.url is still the original unmodified url.
Example:
var express = require('express');
var router = express.Router();
router.use(function(req, res, next) {
console.log('Before Rewrite',req.url);
if(/^\/v[1-9]/.test(req.originalUrl) ) {
console.log('Rewritten');
req.url = '/api' + req.originalUrl;
}
console.log('After Rewrite',req.url);
next();
});
router.use('/api', function(req, res, next) {
console.log('Next', req.url);
next();
});
With an example url of '/v3/foo' the following is output to console:
Before Rewrite /v3/foo
Rewritten
After Rewrite /api/v3/foo
Next /v3/foo
Any thoughts on why the request changes do not persist on to the next middleware?
Thanks to the link from #kelz to the Express next() code, I have a better understanding of how Express handles urls. It seems that req.url is writeable because Express removes the root of the url when matching. For example with an original url of '/foo/bar', if you have this:
router.use('/foo', someMiddleWare);
Then req.url within someMiddleWare will now be '/bar' i.e. with the matched root removed. This is why we have req.originalUrl (which is not writeable) to persist the unaltered url.
As my original approach to rewriting the url can't work, I opted for a simpler solution:
router.all(/^\/v[1-9]/, function(req, res) { res.redirect('/api' + req.originalUrl); });
This way, after the redirect, the req.originalUrl is as it should be for my later middleware.
You are not sharing the same object in both functions. The moment you change the value of req in the middleware function the scope is bound only inside that function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
Example:
function doAThing(a) {
a = 5;
return;
}
var n = 4;
doAThing(n);
console.log(n); // 4

Run the script in node.js only after user select file in html

I am using node.js to launch a serve so that my html can communicate with R code. But I am facing a problem on node.js. In my html page, I have a browse selection button, user can use it to choose the data file they want to read into html and node.js will pass the file name to R, so R code will read data from the selected data file and then run the analytics model. But as i only have very basic knowledge of Node.js, so currently, r code would run only when I open the followling link "localhost:3000/vis/rio". So my question is how to make node.js run the R code in background automatically when the data file has been selected. Thank you very much for your help in advance.
Here are my codes:
Javascript-(sending the file name to node.js):
var dataset,availableTags;
function handleFileSelect(evt) {
var file = evt.target.files[0];
$.ajax({ //getting the file name and update to node.js
type:'post',
url:"/getFileName",
data:{filename:file.name}
});
Papa.parse(file, { //papa is a library I used to read csv file
header: true,
dynamicTyping: true,
complete: function(results) {
dataset=results.data;
}
});
}
$(document).ready(function(){
$("#csv-file").change(handleFileSelect);
});
Node.js script:
serve.js:
var express=require('express');
var path = require('path');
var vis = require('./routes/vis');
var index = require('./routes/index');
var bodyParser=require('body-parser');
var app=express();
require('./routes/main')(app);
app.get('/vis/rio',vis.rio); **//this is a package used to get connection with Rserve**
app.set('views',__dirname + '/views');
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded());
app.post("/getFileName",index.getFileName); **//this is the script to get the file name, it is from index.js**
var server=app.listen(3000,function(){
console.log("Express is running on port 3000");
});
index.js // this is the js file for getting file name
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
});
getFileName=function (req,res){
global.filename=req.body.filename; **//this is the global variable which stores the file name**
res.send('true');
};
module.exports = {router:router,getFileName:getFileName};
vis.js // this is the file used to connect with Rserve and pass the name to R code
var rio = require("rio");
var arg={};
exports.rio = function(req, res){
arg={names:[global.filename]};
console.log(arg);
options = {
entryPoint:"nameoffile",
data: arg,
host : "127.0.0.1",
port : 6311,
callback: function (err, val) {
if (!err) {
console.log("RETURN:"+val);
return res.send({'success':true,'res':val});
} else {
console.log("ERROR:Rserve call failed")
return res.send({'success':false});
}
},
}
rio.enableDebug(true);
rio.sourceAndEval(__dirname + "/Rcode/test.R",options);
};
It looks like you aren't calling out to /vis/rio at any point when you make the call out to your server.
You'll either need to make a second call on the client side to /vis/rio or if you want to use that section, you can import/require the module in index.js and include it in getFileName and just call out to the function there before you return the file. I'm not super familiar with rio, but I don't see any access point in your code to that function.
Edit: If I understand what you're trying to do correctly, you can always make a second request (to /vis/rio) in the success callback of your first ajax call.

Proxy with Express.js + Request.js

I started writing a proxy server with Express.js and Request.js
The server runs on port 900 and does:
Serve static content from path /prototype
For certain calls
forward the request to another server (on port 8080)
Resolves
/prototype/[this|that] to the corresponding static html
/prototype/this.html and /prototype/that.html.
Also serve this.html as default for /prototype
I managed to achieve 1) - 3) with following code:
var express = require('express'), path = require('path'), request = require('request');
var app = express();
app.get('/prototype', function(req, res, next) {
// a) request(req.protocol + '://' + req.get('host') + req.url + '/this').pipe(res);
// b) res.redirect('/prototype/this');
});
app.get('/prototype/:path(this|that)', function(req, res, next) {
var resolvedUrl = req.protocol + '://' + req.get('host') + req._parsedUrl.pathname + '.html';
request(resolvedUrl).pipe(res);
});
app.get('/prototype/sample/:path', function(req, res, next) {
var resolvedUrl = 'http://localhost:8080' + req.url;
request(resolvedUrl).pipe(res);
});
app
.use('/prototype', express.static(path.resolve(__dirname, '../')))
.use(app.router)
.use(express.errorHandler({
dumpException: true,
showStack: true
}));
module.exports = app;
My folder structure is
prototype
|_css
| |_<css files>
|_js
| |_<js files>
|_this.html
|_that.html
this.html refers to the files in css and in js
<link rel="stylesheet" href="css/app.css" />
<script data-main="js/app.js" src="js/require.js"></script>
In order to achieve 4) I tried to request the /prototype/this path when /prototype is requested, however then the js and css are requested from /js/app.js and /css/app.css instead of /prototype/js/app.js and /prototype/css/app.css which fails. (See a) in the code comment).
The behavior I would like to achieve can be achieved with b) but I don't want to have the URL bar in the browser changed to /prototype/this but keep it in /protoype.
Any nice way to do this?
Turned out to be a missing trailing slash.. after /prototype.
Without the trailing slash I was overriding the behaviour that redirected to the directory with trailing slash when requesting directory path without trailing slash.
Changed it to the following and it works like a charm.
app.get('/prototype/', function(req, res, next) {
// a) request(req.protocol + '://' + req.get('host') + req.url + '/this').pipe(res);
// b) res.redirect('/prototype/this');
});

Define pattern route for node js

I am using Node.js and express to make a web chat application. I have a question about routing.
My route is:
app.get("/", function( req, res ) {
res.sendfile( __dirname + "/index.html" );
});
And as far as I know it means all clients should go to http://www.example.com/index.html page to access the chat. ( maybe, not sure )
Is it possible to have a pattern URL like this:
app.get("/*", function( req, res ) {
res.sendfile( __dirname + "/*" );
});
so that any user can access to chat on any URL
In short: Something like facebook chat. which is accessible in all pages.
thanks in advance
Is it possible to have a pattern URL like this:
Yeah, Express supports exactly the route you listed. The documentation refers to it as an "unnamed wild-card."
// GET /foo -> (200) "foo"
// GET /baz/qux -> (200) "baz/qux"
app.get('/*', function (req, res) {
res.send(req.params[0]);
});
Though, for your example, Express/Connect includes a static() middleware for serving files by combining the URL path to a base directory:
app.use(express.static(__dirname));
Something like facebook chat. which is accessible in all pages.
This wouldn't necessarily have much of a relation to routing. Your application may need a route to gather a list of contacts or other persisted data, but the actual "chat" will be managed separately.
In general, it would depend on including common content in each page -- perhaps via a "layout" or "inheritance" if you're using views/templates -- to display the form for inputting message and an area to display chat history. Also, a fair amount of the "work" for the chat will have to be done client-side.
A quick example would be to serve the same file for multiple URLs:
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
app.get('/*', function (req, res) {
res.sendfile(__dirname + '/chat.html');
});
var chat = io
.of('/chat') // namespace
.on('connection', function (socket) {
socket.on('message', function (data) {
chat.emit('message', data);
});
});
server.listen(3000);
And, in that file:
<div id="chat-log"></div>
<form id="chat-send">
<input name="message">
<input type="submit" value="Send">
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
var chatForm = document.getElementById('chat-send');
var chatLog = document.getElementById('chat-log');
var chatSocket = io.connect('/chat');
chatSocket.on('message', function (data) {
chatLog
.appendChild(document.createElement('div'))
.appendChild(document.createTextNode(data.text));
});
chatForm.onsubmit = function () {
chatSocket.emit('message', {
text: chatForm.message.value
});
chatForm.message.value = '';
chatForm.message.focus();
return false;
};
</script>
Then, from any address (GET /, GET /foo, GET /bar/baz/qux), you can access chat.
You're probably going to want to use a url param like this:
app.get("/:chatter",function(req,res){
console.log("the chatter is",req.param('chatter'));
res.sendfile( __dirname + "/index.html" );
...
});

Correct way to Load JS Files With HTML files through NodeJS

I cant get the contents included in the head of the served defualt.htm page to "work". The html loads in the dom, just the CSS and JS files fail. Is there a better alternative? Id like to keep the solution within NodeJS but alternatively open to socket.io and express as well.
Thanks, below is what im using.
NodeJS Serving the Page
var http = require('http'),
fs = require('fs');
fs.readFile(__dirname+'/default.htm', function (err, html) {
if (err) {
throw err;
}
http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html"});
response.write(html);
response.end();
}).listen(port.number);
});
Default.html Page
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="objects/css/site.css" type="text/css" />
<script src="objects/js/jquery.min.js" type="text/javascript"></script>
<script src="objects/js/site.min.js" type="text/javascript"></script>
</head>
<body></body>
</html>
Your Javascript and styles are failing because they don't exist. Your current webserver is only sending a single route, the root route. Instead you'll need to allow the use of multiple routes. ExpressJS does this for you in a simpler way, but still very possible without it.
var http = require('http');
var fs = require('fs');
var server = http.createServer(function(request, response){
var header_type = "";
var data = "";
var get = function (uri, callback) {
// match `request.url` with uri with a regex or something.
var regex = uri;
if (request.url.match(regex)) {
callback();
}
};
var render = function (resource) {
// resource = name of resource (i.e. index, site.min, jquery.min)
fs.readFile( __dirname + "/" + resource, function(err, file) {
if (err) return false; // Do something with the error....
header_type = ""; // Do some checking to find out what header type you must send.
data = file;
}
};
get('/', function(req, res, next) {
// Send out the index.html
render('index.html');
next();
});
get('/javascript.min', function(req, res, next) {
render('javascript.js');
next();
});
});
server.listen(8080);
This might get you started, but you'll have to implement some things like next() yourself. A pretty simple solution, but a working one.
Another solution for responding to static files would be to create a catcher within the http.createServer callback. Within the get method, if the uris don't match, then you would look within a public folder matching the full uri to the file system structure.
I'm going to throw my two cents in here as well.
The way I solved the same problem with serving static files is that I started using the Paperboy module, which is deprecated in favor of the Send module nowadays.
Anyhoo, the way I solved it was to "hijack" the request before it went into my GET method and check the path of it.
The way I "hijack it" is as follows
self.preProcess(self, request, response);
and
preProcess: function onRequest(app, request, response){ //DO STUFF }
If the path contained the STATICFILES dir, I would do a diffrent serving of files otherwise I'd go with the "html"-path. Below is the //DO STUFF of the preProcess() function
var path = urllib.parse(request.url).pathname;
if(path.indexOf(settings.STATICFILES_DIR) != -1) {
path = settings.STATICFILES_DIR;
requestedFile = request.url.substring(request.url.lastIndexOf('/') + 1, request.url.length);
return resolver.resolveResourceOr404(requestedFile, request, response);
}
There might be a better way of doing this but this works like a charm for the things that I need it to do.
Using the Paperboy module I then, using the resolver.resolveResourceOr404(); function
deliver the file like so
resolveResourceOr404 : function (filename, httpRequest, httpResponse) {
var root = path.join(path.dirname(__filename), '');
paperboy.deliver(root, httpRequest, httpResponse)
.error(function(e){
this.raise500(httpResponse);
})
.otherwise(function(){
this.raise404(httpResponse);
});
}
Well, you are serving your default.htm file on all requests. So, when the browser asks for objects/js/jquery.min.js, your server returns the contents of default.htm.
You should really consider using express or some other framework.
what about trying this:
var http = require('http');
var fs = require('fs');
var path = require('path');
http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './')
filePath = './index.html';
var extname = path.extname(filePath);
var contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
case '.json':
contentType = 'application/json';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
contentType = 'image/jpg';
break;
case '.wav':
contentType = 'audio/wav';
break;
}
fs.readFile(filePath, function(error, content) {
if (error) {
if(error.code == 'ENOENT'){
fs.readFile('./404.html', function(error, content) {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
});
}
else {
response.writeHead(500);
response.end('Sorry, check with the site admin for error: '+error.code+' ..\n');
response.end();
}
}
else {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
}
});
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
You're better off with using Express for this kind of stuff.
Something like this will do the job.
App.js
var express = require('express')
, http = require('http')
, path = require('path');
var app = express();
//Configure Your App and Static Stuff Like Scripts Css
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views'); // Your view folder
app.set('view engine', 'jade'); //Use jade as view template engine
// app.set("view options", {layout: false});
// app.engine('html', require('ejs').renderFile); //Use ejs as view template engine
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(app.router);
app.use(require('stylus').middleware(__dirname + '/public')); //Use Stylus as the CSS template engine
app.use(express.static(path.join(__dirname, 'public'))); //This is the place for your static stuff
});
app.get('/',function(req,res){
res.render('index.jade',{
title:"Index Page
}
});
Index is a jade template page.Which renders into static html and works pretty good with express.
For a global static header to all of your pages you can make a template like this and include it in any.
static_header.jade
doctype 5
html
head
title= title
script(src='/javascripts/jquery-1.8.2.min.js')
block header
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
And finally your index.jade which is using the static_header and own dynamic header with its own scripts.
extends static_header
block header
script(src='/javascripts/jquery-ui-1.9.1.custom.js')
script(src='http://jquery-ui.googlecode.com/svn/trunk/ui/i18n/jquery.ui.datepicker-tr.js')
link(rel='stylesheet',href='/stylesheets/jquery-ui-1.9.1.custom.min.css')
block content
h1= title
Put both of the files in your views folder and ready to roll.

Categories