WebFund 2016W Lecture 9
Video
The video from the lecture given on February 4, 2016 is now available.
Notes
Agenda
- We are now a week away from the midterm (February 11th, 2016)
- The solutions for Assignment 3 will be reviewed in class on Tuesday, February 9th
- In this class the focus was on extending the functionality of tinyweb.js to that of form-demo.js and examforms.js in lieu of introducing new topics before the midterm
Version Control
- Question: How many people have used CVS or Github previously? Has version control been covered in previous courses?
- Majority of class indicated that version control has not been a topic covered in previous courses.
- Pros of version control:
- The ability to create backups and branch code
- Github is a great resource for powerful collaboration, code review, and code management for open source and private projects. Public projects are always free. (https://github.com/ )
- Git was originally developed for the Linux kernel.
- Code Academy has a Github tutorial - https://www.codecademy.com/learn/learn-git
- Diff command was recommended – analyzes two files and prints the lines that different. (diff file1.js file2.js)
- e.g diff ~/Documents/Class/WebFund/2016W/code tinwebserver.js tinywebserver_solution
tinyweb.js vs form-demo.js
- They can both serve static files
- tinyweb.js:
- tinyweb.js serves static content from the directory specified as the options directory
- A file on disk is served for a particular URL. As a result, the content always remains the same (static content)
- form-demo.js:
- form-demo serves static content form a public directory
- Can handle form submissions... this depends upon handling dynamic content since the whole point is to change the state of the web server
- Supports jade templates
- Can run code to generate pages... meaning that there are functions that are run every time a request is made for a particular URL (dynamic content)
- e.g in form-demo/routes/index.js:
router.get('/', function(req, res, next) {
res.render('index', { title: 'COMP 2406 Simple form demo' });
});
Coding Exercise - Adding More Functionality To tinyweb.js
- To get started, it’s better to initially create data structures and then create code to support the data structures
- First step of emulating form-demo was to create the framework for the routing functions (‘/’ , ‘/add’, “/list’).
- Must follow the definition of
var route_get...
,var route_post...
andvar_route_list...
functions in order to avoid the generation of “Type Error: the Route is not a function error.
- Must follow the definition of
// routes is a mapping of the url’s we want to come in and the code we want to run when the url’s are run.
var routes = {
‘/’: route_index,
‘/add’ : route_add,
‘/list’ : route_list
}
- The following code was copied from index.js:
router.get('/', function(req, res, next) {
res.render('index', { title: 'COMP 2406 Simple form demo' });
});
router.post('/add', function(req, res) {
var obj = { name: req.body.name,
city: req.body.city,
country: req.body.country,
birthday: req.body.birthday,
email: req.body.email };
state.push(obj);
res.render('add', { title: 'Person just added', item: obj });
});
router.get('/list', function(req, res) {
res.render('list', { title: 'People Listing', items: state});
});
- The above code was pasted into tinyweb-forms.js after
var options;
and before the definition of theroutes
object - The code was adapted as shown below
- Note the
next
argument was removed from functions since this argument is not needed in our version
- Note the
//The purpose of these functions is to render a template using jade and respond with the rendered HTML
//We will be making modifications to achieve this
//This one has been partially changed and now provides a response with a static web page
var route_index = function(req, res) {
//res.render('index', { title: 'COMP 2406 Simple form demo' });
respond(req, res, 200, “<html><body><h1>Success!</h1></body></html>”,”text/html”); //tiny web page
}
var route_add = (function(req, res) {
var obj = { name: req.body.name,
city: req.body.city,
country: req.body.country,
birthday: req.body.birthday,
email: req.body.email };
state.push(obj);
res.render('add', { title: 'Person just added', item: obj });
}
var route_list = (function(req, res) {
res.render('list', { title: 'People Listing', items: state});
}
- To get the above functions called, the code to tinyweb_forms.js was modified in the
request_handler
function just after the following if statement:
if(options.aliases.hasOwnProperty(url)){
myConsole.log(“Aliasing “ + url + “ to “ + options.aliases[url]);
url = options.aliases[url];
}
- The code that was added is:
if (routes.hasOwnProperty(url)) {
theRoute = routes[url]; // look it up and send the info the return and response
return theRoute(request, response); // arguments used from var request_handler
}
- We do not use the serve file function since we are generating content dynamically
- To make sure the code ran correctly, the tinyweb-options.json, error.html and tinyweb-forms.js files were moved to the same folder
- Furthermore, the tinyweb-options.json file was modified from
{
“host”: “localhost”,
“port”: 8000,
”index”: ”index.html”,
”docroot” : “.”,
“logged-headers”: [”user-agent”, “referrer”],
”errorpage”: ”error.html”,
”aliases”: {
”/index.html”: “/hello.html”,
“/”: “/hello.html”,
“/really/cool.html”: “/notcool.html”
}
}
- To:
{
“host”: “localhost”,
“port”: 8000,
”index”: ”index.html”,
”docroot” : “.”,
“logged-headers”: [”user-agent”, “referrer”],
”errorpage”: ”error.html”,
”aliases”: {
”/index.html”: “/”,
“/hello.html”: “/”,
“/really/cool.html”: “/notcool.html”
}
}
- In order to render the Jade template, the following line was added at the top of the script:
var jade = require(‘jade’);
- In order to load Jade in this way, you must run the following command in the folder that contains the tinyweb-forms files:
npm install jade
- A node_modules sub folder will be created
- The jade files from form-demo/views(templates) can now be moved over to the tinyweb-forms folder (main folder)
- The
route_index
function is then modified as follows to in order to use the Jade template- Note that an async function is used to ensure this is done in a non-blocking fashion... we want the webserver to remain responsive for other incoming requests
var route_index = function(req, res) {
var content, template, options;
var render_template = function(err, template){
content = jade.render(template, options);
respond(req, res, 200, content, ”text/html”);
}
options = {pretty: true,
title: 'COMP 2406 Tinyweb Forms Demo'};
return fs.readFile(“index.jade”, ”utf-8”, render_template);
}
- When the code above was run errors that were generated were:
- Error #1:
- Error: Jade:1
- >1 extends layout….the “filename” option is required to use ”extends” with ””relative” paths... because it doesn’t know what file to load
- Solution #1: add a reference to index.jade
- Error #1:
var route_index = function(req, res) {
var content, template, options;
var render_template = function(err, template){
content = jade.render(template, jade_options);
respond(req, res, 200, content,”text/html”);
}
jade_options = {pretty: true,
title: 'COMP 2406 Tinyweb Forms Demo',
filename: ‘index.jade’};
return fs.readFile(jade_options.filename, ”utf-8”, render_template);
}
// The calls to fs.readFile(), jade.render() and respond() are all needed to get the functionality of the res.render() which was contained exam-forms
- Error #2:
- A 404 error was generated for style.css
- Solution #2:
- Copy the stylesheet over to the form-demo folder
- Once this was done, prettier fonts were generated and the 404 error was no longer generated
- Error and Solution #3: Unexpected identifier
- Generated because there was a comma missing at the end of title:
'COMP 2406 Tinyweb Forms Demo',
in thejade_options
object
- Generated because there was a comma missing at the end of title:
- Error #2:
- Next, we moved on to modifying code to redirect the /add route to /list
var route_add = (function(req, res) {
// var obj = { name: req.body.name,
// city: req.body.city,
// country: req.body.country,
// birthday: req.body.birthday,
// email: req.body.email };
//state.push(obj);
//res.render('add', { title: 'Person just added', item: obj });
myConsole.log(“302” + “\t” + req.method + “\t” + req.url);
res.writeHead(302, {
“Content-Type”: “text/html”
“Location”: “/list”
});
return res.end();
respond(req, res, 200, content, “text/html”);
}
var route_list = (function(req, res) {
res.render('list', { title: 'People Listing', items: state});
}
- By setting the appropriate HTTP response code and response headers, we indicate to the client's browser that it should make another request in order to achieve the redirection
- In examforms.js,
res.redirect(‘/list’);
handles this same functionality for redirection
To Summarize
- The changes that we made in this exercise include:
- Giving tinyweb the ability to run code to dynamically generate response for incoming requests
- Bringing in the functionality to render templates and respond with the rendered HTML
- Implementing redirection in response to some requests
- The code in this exercise does not:
- Save the state of the data (the data does not persist after the server is shut down)
- Store the state object (the people added in the form)
- Render the contents of the list
Additional Notes
- Don’t use globals for data unless you are thinking explicitly that the data will be shared
- e.g the
state
array used in this code may not be well-suited as a global variable
- e.g the
- Web servers have databases so that they can persistently store their data (don’t want the data to go away) and concurrently access the data
- This is a better alternative for storing the state data
- The code modifications made in this exercise to bridge the gap between tinyweb.js and examforms.js were very specific
- examforms.js is far more robust in terms of how it’s coded