WebFund 2016W Lecture 12: Difference between revisions
Created page with "==Video== The video for the lecture given on February 25, 2016 [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/lectures/comp2406-2016w-lec12-25Feb2016.mp4 is now avai..." |
No edit summary |
||
(One intermediate revision by one other user not shown) | |||
Line 2: | Line 2: | ||
The video for the lecture given on February 25, 2016 [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/lectures/comp2406-2016w-lec12-25Feb2016.mp4 is now available]. | The video for the lecture given on February 25, 2016 [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/lectures/comp2406-2016w-lec12-25Feb2016.mp4 is now available]. | ||
==Code== | |||
Below is examforms.js modified to have MongoDB support. You can also [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/code/examforms-mongodb.zip download the full version]. | |||
<source lang="javascript" line> | |||
var http = require('http'); | |||
var express = require('express'); | |||
var bodyParser = require('body-parser'); | |||
var logger = require('morgan'); | |||
var port = 3000; | |||
var mc = require('mongodb').MongoClient; | |||
var db, formsCollection; | |||
var state = []; | |||
var app = express(); | |||
app.set('view engine', 'jade'); | |||
app.set('views', __dirname); | |||
app.use(logger('dev')); | |||
app.use(bodyParser.urlencoded({ extended: false })); | |||
var connectCallback = function(err, returnedDB) { | |||
if (err) { | |||
throw err; | |||
} | |||
db = returnedDB; | |||
formsCollection = db.collection('forms'); | |||
} | |||
mc.connect('mongodb://localhost/examforms', connectCallback); | |||
app.get('/', function(req, res, next) { | |||
res.render('index', { title: 'COMP 2406 Exam form demo' }); | |||
}); | |||
app.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); | |||
formsCollection.insert(obj, null); | |||
res.redirect('/list'); | |||
}); | |||
app.get('/list', function(req, res) { | |||
var findCallback = function(err, dbstate) { | |||
if (err) { | |||
console.log("Couldn't query database for some reason"); | |||
return; | |||
} | |||
res.render('list', { title: 'People Listing', items: dbstate}); | |||
} | |||
formsCollection.find({}).toArray(findCallback); | |||
}); | |||
var serverUp = function() { | |||
console.log("ExamForms listening on port " + port); | |||
} | |||
var serverDown = function() { | |||
console.log("Server shutting down."); | |||
process.exit(0); | |||
} | |||
var server = http.createServer(app); | |||
server.listen(port); | |||
server.on('listening', serverUp); | |||
process.on('SIGINT', serverDown); | |||
</source> | |||
==Student Notes== | |||
===Midterms Solutions=== | |||
*Make sure to read the questions carefully as they may use slightly different patterns from the things you have seen previously in class and in the tutorials and assignments<br><br> | |||
<strong>1. What is the type of the values associated with all of the symbols referenced in line examforms.js:44? Specifically, what is the type of the values bound to server, on, and serverUp?</strong> | |||
A: server: object, on: function, serverUp: function | |||
<br> | |||
<br> | |||
<strong>2. If I create a line 46 in examforms.js (i.e., add a line to the end of the file) that says ’console.log(”Finished!”);’, when will finished! be printed relative to the other output of the program? Explain your reasoning.</strong> | |||
A: “Finished!” should generally be printed first, before any other output, because all of the other output is produced by callbacks and they are normally executed after the main body of the file has been evaluated. It is acceptable to say that you don’t actually know when it will be printed since you can’t know for certain when callbacks will be invoked; however, the mainly single-threaded computation model of JavaScript makes this sort of interleaving very unlikely (unless you use something like web workers). | |||
<br> | |||
<br> | |||
<em>We had an example like this from class before the Midterm:</em><br> | |||
<strong>3. If you change the app.post() call to an app.get() call on examforms.js:19, how would the program no longer function properly? Is it possible to repair it without changing this line back to app.get()? (yes or no)</strong> | |||
A: It would no longer function properly because the form submission generates a “POST /add” HTTP request and that HTTP request is no longer recognized by the server. Yes. (If you change the form’s method (index.jade:8) to get and make the function access parameters as passed by GETs (req.query rather than req.body) then it would be fixed.) | |||
<br> | |||
<br> | |||
<strong>4. (2) As written, examforms returns a 404 error for requests to style.css. What is wrong with examforms? How could you fix it?</strong> | |||
A: examforms has no way of serving static files. You’d either have to create a custom route that would load the style sheet manually, or you’d have to include the line normally used in express node applications to add static server support. (Specifically, you’d add app.use (express.static( dirname)); around examforms.js:14.) In other words, the line that adds functionality to serve static files is currently missing. | |||
<br> | |||
<br> | |||
<strong>5. The request object (req) passed in to route handlers has a “headers” property, just as the request objects in tinywebserver.js. Given this fact, how could you change examforms so that it uses a different style sheet when receiving requests from iPhones? (Describe the general strategy, no need for all the implementation details.)</strong> | |||
<em>//We saw this previously for assignment 3 and this question wasn’t a question of coding it to answer, you should simply understand the concepts behind it.</em> | |||
A: We would just have to check the value of req.headers[“user-agent”] to see if it contained the string “iPhone” in each route handling function. We’d then have to either use different jade files for iPhones that used the different stylesheet, or we’d move the stylesheet from layout.jade and instead pass it in like we do the title of the page. | |||
<br> | |||
<br> | |||
<strong>6. When a web browser loads a standard web page, what type of HTTP request does it make of the server? And, if the requested URL is invalid (i.e., page not found), what is the standard response code?</strong> | |||
A: HTTP GET; 404 | |||
<br> | |||
<br> | |||
<strong>7. How could you change examforms so you could specify the PORT on the command line, e.g. you’d run node examforms.js 7000 to have it listen on port 7000? If no port is specified it should listen on port 3000.</strong> | |||
<em>//Student question: Will there be any marks for talking about environment variables because it was what was learned in tutorial?<br> | |||
//Answer: No, the question has nothing to do with environment variables. You must answer specifically what the question asked.</em> | |||
A: Add a line to set the port variable based on the value of process.argv[2], e.g. var port = process.argv[2] || 3000; | |||
<br> | |||
<br> | |||
<strong>8. What is the purpose of the Content-Type: HTTP response header?</strong> | |||
A: It tells the browser the type of content being returned, whether it is a plain text file (text/plain), an HTML document (text/HTML), an image (image/jpeg), etc. | |||
<br> | |||
<br> | |||
<strong>9. When the call to app.get on lines 15–17 of examforms.js returns, what work has it accomplished? Specifically what data (if any) has been returned to the requesting web client? Why?</strong> | |||
<em>//Note that the function is only being defined, not run. It is only run when a request is received (which is not what has happened here)!!</em> | |||
A: After these lines have been written a function has been defined and it has been registered as a callback for an HTTP GET request. The anonymous function has not been called; it will only be invoked when there is an HTTP GET request. | |||
<br> | |||
<br> | |||
<strong>10. When is the function serverDown(), on lines 37-40 of examforms.js, called? How can a user cause this function to be called?</strong> | |||
A: This function is called when the server receives a SIGINT signal. This signal is generated when a user presses Ctrl-C at the command line. | |||
<br> | |||
<br> | |||
<strong>11. What would happen to examforms if we deleted layout.jade:6 (block header)? What about layout.jade:8 (block content)?</strong> | |||
A: If you delete line 6, nothing would happen as this block isn’t used in any of the templates. Deleting line 8 would make the body of all pages blank. | |||
<br> | |||
<br> | |||
<strong>12. The each statement on list.jade:16 is a loop. What does this loop do? Where is it executed (server or browser)?</strong> | |||
A: This loop generates a table line for every person stored in the state array (passed to the template on examforms.js:30). It runs/ is EXECUTED on the server as it is a Jade loop. The browser simply gets a fully populated table. | |||
<br> | |||
<br> | |||
<strong>13. What would happen (if anything) if you removed two spaces from the beginning of list.jade:25, button(type="submit") Home?</strong> | |||
A: By removing these spaces the button is no longer indented under the form on line 24; thus now it is a submit button that is no longer enclosed inside of an HTML form. Thus while the button will still be present in the page, clicking it will now do nothing. | |||
<br> | |||
<br> | |||
<strong>14. Do web browsers normally run/interpret Jade code? Explain briefly.</strong> | |||
A: Web browsers do not interpret Jade code; instead, they interpret the HTML that is produced by compiling Jade templates. | |||
<br> | |||
<br> | |||
<strong>15. What does res.redirect(/list); on line 26 of examforms.js do? Specifically, what response is (eventually or immediately) sent back to the browser after this line is executed? Explain briefly.</strong> | |||
A: This line causes a 302 (permanent redirect) response to be sent back to the browser with /list as the new location. The browser then proceeds to do an HTTP GET /list which then causes the callback for /list to be run on the server (and return the corresponding page). | |||
===Using a Database with Examforms=== | |||
*Question: We want to write records to the database, how? | |||
*We will need to modify the code in order to give it the ability to interact with the database | |||
====Establishing a Connection==== | |||
*First we add the code needed to establish a connection to the database: | |||
<code><pre> | |||
var mc = require(‘Mongodb’).MongoClient; | |||
mc.connect(‘mongodb://localhost/examforms’, connectCallback); | |||
</pre></code> | |||
*This allows you to connect to the examforms database via the Mongo server running on localhost using the mongodb protocol (as specified by the first argument to the <code>connect()</code> method) | |||
*Note that we need the Mongo server running in order for this code to run properly | |||
*Recall that if you’re using the course VM, Mongo is installed; if using you’re own machine, you must install Mongo to be able to use it | |||
**We can verify that Mongo is running by opening a new terminal and typing <code>mongo</code> to run the terminal client | |||
**If the Mongo server is not running, we will see a message stating that the connection has failed | |||
**We would get a similar error if our application tries to establish a connection while Mongo is not running | |||
*Next, we need to implement connectCallback | |||
<code><pre> | |||
var connectCallback = function(err, returnedDB) { | |||
if (err) { | |||
throw err; | |||
} | |||
db = returnedDB; | |||
formsCollection = db.collection('forms'); | |||
} | |||
</pre></code> | |||
*<code>db</code> and <code>formsCollection</code> should be defined beforehand at the global scope so that we will have access to them in other callback functions later | |||
====Inserting Documents into the Database/Collection==== | |||
*Now we can add code to store the data that we receive from form submissions (currently stored in the <code>obj</code> variable) | |||
*This code should be added inside the function defined for the /add route | |||
<code><pre> | |||
formsCollection.insert(obj, null); //pass null because we are being explicit and don’t want to pass anything as the callback function | |||
res.redirect(‘/list’); | |||
</pre></code> | |||
*To verify if we have properly inserted anything into the database, we can use the terminal client | |||
*Mongo has many commands that can be used to tell you the current state of your database | |||
*The db.serverStatus() command (from the terminal client) returns an overview of the status of the database. Includes disk usage, memory use, connection, journaling, and index access | |||
*For other administration requirements that you may run across visit [https://docs.mongodb.org/manual/administration/monitoring/ https://docs.mongodb.org/manual/administration/monitoring/] | |||
*To check if the document was actually stored in the collection, in the terminal client type: | |||
<code><pre> | |||
use examforms | |||
db.forms.find() | |||
</pre></code> | |||
*This will display the documents in the forms collection within the examforms database | |||
*To clear data from the collection, you can use the <code>drop()</code> method of the collection in order to drop (remove) the entire collection | |||
====Querying the Database/Collection==== | |||
*In order to now display the list of people that have been entered via the form, we need to be able to query the database | |||
*We can add the following code to the function defined for the /list route | |||
<code><pre> | |||
formsCollection.find({}).toArray(findCallback); | |||
</pre></code> | |||
*The argument passed to the <code>find()</code> method is our query | |||
**Here, the query is an empty object so every document will be matched | |||
*The return value of the <code>find()</code> method is a cursor object used to iterate through the results | |||
*We are calling the <code>toArray()</code> method of the cursor object to get all the results as an array | |||
**The argument to this method is the callback function where we will have access to the array | |||
**Since the callback function is the only place where we have access to the results, we must send a response to the client from there | |||
***This means that the callback function must be defined within the scope of the response object | |||
*Look into MongoDB API online for help with assignment 4, specifically <code>toArray()</code>: [https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toarray https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toarray] |
Latest revision as of 16:53, 1 March 2016
Video
The video for the lecture given on February 25, 2016 is now available.
Code
Below is examforms.js modified to have MongoDB support. You can also download the full version.
var http = require('http');
var express = require('express');
var bodyParser = require('body-parser');
var logger = require('morgan');
var port = 3000;
var mc = require('mongodb').MongoClient;
var db, formsCollection;
var state = [];
var app = express();
app.set('view engine', 'jade');
app.set('views', __dirname);
app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
var connectCallback = function(err, returnedDB) {
if (err) {
throw err;
}
db = returnedDB;
formsCollection = db.collection('forms');
}
mc.connect('mongodb://localhost/examforms', connectCallback);
app.get('/', function(req, res, next) {
res.render('index', { title: 'COMP 2406 Exam form demo' });
});
app.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);
formsCollection.insert(obj, null);
res.redirect('/list');
});
app.get('/list', function(req, res) {
var findCallback = function(err, dbstate) {
if (err) {
console.log("Couldn't query database for some reason");
return;
}
res.render('list', { title: 'People Listing', items: dbstate});
}
formsCollection.find({}).toArray(findCallback);
});
var serverUp = function() {
console.log("ExamForms listening on port " + port);
}
var serverDown = function() {
console.log("Server shutting down.");
process.exit(0);
}
var server = http.createServer(app);
server.listen(port);
server.on('listening', serverUp);
process.on('SIGINT', serverDown);
Student Notes
Midterms Solutions
- Make sure to read the questions carefully as they may use slightly different patterns from the things you have seen previously in class and in the tutorials and assignments
1. What is the type of the values associated with all of the symbols referenced in line examforms.js:44? Specifically, what is the type of the values bound to server, on, and serverUp?
A: server: object, on: function, serverUp: function
2. If I create a line 46 in examforms.js (i.e., add a line to the end of the file) that says ’console.log(”Finished!”);’, when will finished! be printed relative to the other output of the program? Explain your reasoning.
A: “Finished!” should generally be printed first, before any other output, because all of the other output is produced by callbacks and they are normally executed after the main body of the file has been evaluated. It is acceptable to say that you don’t actually know when it will be printed since you can’t know for certain when callbacks will be invoked; however, the mainly single-threaded computation model of JavaScript makes this sort of interleaving very unlikely (unless you use something like web workers).
We had an example like this from class before the Midterm:
3. If you change the app.post() call to an app.get() call on examforms.js:19, how would the program no longer function properly? Is it possible to repair it without changing this line back to app.get()? (yes or no)
A: It would no longer function properly because the form submission generates a “POST /add” HTTP request and that HTTP request is no longer recognized by the server. Yes. (If you change the form’s method (index.jade:8) to get and make the function access parameters as passed by GETs (req.query rather than req.body) then it would be fixed.)
4. (2) As written, examforms returns a 404 error for requests to style.css. What is wrong with examforms? How could you fix it?
A: examforms has no way of serving static files. You’d either have to create a custom route that would load the style sheet manually, or you’d have to include the line normally used in express node applications to add static server support. (Specifically, you’d add app.use (express.static( dirname)); around examforms.js:14.) In other words, the line that adds functionality to serve static files is currently missing.
5. The request object (req) passed in to route handlers has a “headers” property, just as the request objects in tinywebserver.js. Given this fact, how could you change examforms so that it uses a different style sheet when receiving requests from iPhones? (Describe the general strategy, no need for all the implementation details.)
//We saw this previously for assignment 3 and this question wasn’t a question of coding it to answer, you should simply understand the concepts behind it.
A: We would just have to check the value of req.headers[“user-agent”] to see if it contained the string “iPhone” in each route handling function. We’d then have to either use different jade files for iPhones that used the different stylesheet, or we’d move the stylesheet from layout.jade and instead pass it in like we do the title of the page.
6. When a web browser loads a standard web page, what type of HTTP request does it make of the server? And, if the requested URL is invalid (i.e., page not found), what is the standard response code?
A: HTTP GET; 404
7. How could you change examforms so you could specify the PORT on the command line, e.g. you’d run node examforms.js 7000 to have it listen on port 7000? If no port is specified it should listen on port 3000.
//Student question: Will there be any marks for talking about environment variables because it was what was learned in tutorial?
//Answer: No, the question has nothing to do with environment variables. You must answer specifically what the question asked.
A: Add a line to set the port variable based on the value of process.argv[2], e.g. var port = process.argv[2] || 3000;
8. What is the purpose of the Content-Type: HTTP response header?
A: It tells the browser the type of content being returned, whether it is a plain text file (text/plain), an HTML document (text/HTML), an image (image/jpeg), etc.
9. When the call to app.get on lines 15–17 of examforms.js returns, what work has it accomplished? Specifically what data (if any) has been returned to the requesting web client? Why?
//Note that the function is only being defined, not run. It is only run when a request is received (which is not what has happened here)!!
A: After these lines have been written a function has been defined and it has been registered as a callback for an HTTP GET request. The anonymous function has not been called; it will only be invoked when there is an HTTP GET request.
10. When is the function serverDown(), on lines 37-40 of examforms.js, called? How can a user cause this function to be called?
A: This function is called when the server receives a SIGINT signal. This signal is generated when a user presses Ctrl-C at the command line.
11. What would happen to examforms if we deleted layout.jade:6 (block header)? What about layout.jade:8 (block content)?
A: If you delete line 6, nothing would happen as this block isn’t used in any of the templates. Deleting line 8 would make the body of all pages blank.
12. The each statement on list.jade:16 is a loop. What does this loop do? Where is it executed (server or browser)?
A: This loop generates a table line for every person stored in the state array (passed to the template on examforms.js:30). It runs/ is EXECUTED on the server as it is a Jade loop. The browser simply gets a fully populated table.
13. What would happen (if anything) if you removed two spaces from the beginning of list.jade:25, button(type="submit") Home?
A: By removing these spaces the button is no longer indented under the form on line 24; thus now it is a submit button that is no longer enclosed inside of an HTML form. Thus while the button will still be present in the page, clicking it will now do nothing.
14. Do web browsers normally run/interpret Jade code? Explain briefly.
A: Web browsers do not interpret Jade code; instead, they interpret the HTML that is produced by compiling Jade templates.
15. What does res.redirect(/list); on line 26 of examforms.js do? Specifically, what response is (eventually or immediately) sent back to the browser after this line is executed? Explain briefly.
A: This line causes a 302 (permanent redirect) response to be sent back to the browser with /list as the new location. The browser then proceeds to do an HTTP GET /list which then causes the callback for /list to be run on the server (and return the corresponding page).
Using a Database with Examforms
- Question: We want to write records to the database, how?
- We will need to modify the code in order to give it the ability to interact with the database
Establishing a Connection
- First we add the code needed to establish a connection to the database:
var mc = require(‘Mongodb’).MongoClient;
mc.connect(‘mongodb://localhost/examforms’, connectCallback);
- This allows you to connect to the examforms database via the Mongo server running on localhost using the mongodb protocol (as specified by the first argument to the
connect()
method) - Note that we need the Mongo server running in order for this code to run properly
- Recall that if you’re using the course VM, Mongo is installed; if using you’re own machine, you must install Mongo to be able to use it
- We can verify that Mongo is running by opening a new terminal and typing
mongo
to run the terminal client - If the Mongo server is not running, we will see a message stating that the connection has failed
- We would get a similar error if our application tries to establish a connection while Mongo is not running
- We can verify that Mongo is running by opening a new terminal and typing
- Next, we need to implement connectCallback
var connectCallback = function(err, returnedDB) {
if (err) {
throw err;
}
db = returnedDB;
formsCollection = db.collection('forms');
}
db
andformsCollection
should be defined beforehand at the global scope so that we will have access to them in other callback functions later
Inserting Documents into the Database/Collection
- Now we can add code to store the data that we receive from form submissions (currently stored in the
obj
variable) - This code should be added inside the function defined for the /add route
formsCollection.insert(obj, null); //pass null because we are being explicit and don’t want to pass anything as the callback function
res.redirect(‘/list’);
- To verify if we have properly inserted anything into the database, we can use the terminal client
- Mongo has many commands that can be used to tell you the current state of your database
- The db.serverStatus() command (from the terminal client) returns an overview of the status of the database. Includes disk usage, memory use, connection, journaling, and index access
- For other administration requirements that you may run across visit https://docs.mongodb.org/manual/administration/monitoring/
- To check if the document was actually stored in the collection, in the terminal client type:
use examforms
db.forms.find()
- This will display the documents in the forms collection within the examforms database
- To clear data from the collection, you can use the
drop()
method of the collection in order to drop (remove) the entire collection
Querying the Database/Collection
- In order to now display the list of people that have been entered via the form, we need to be able to query the database
- We can add the following code to the function defined for the /list route
formsCollection.find({}).toArray(findCallback);
- The argument passed to the
find()
method is our query- Here, the query is an empty object so every document will be matched
- The return value of the
find()
method is a cursor object used to iterate through the results - We are calling the
toArray()
method of the cursor object to get all the results as an array- The argument to this method is the callback function where we will have access to the array
- Since the callback function is the only place where we have access to the results, we must send a response to the client from there
- This means that the callback function must be defined within the scope of the response object
- Look into MongoDB API online for help with assignment 4, specifically
toArray()
: https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toarray