HTTP and Form Handling with Express
In order to follow along with this activity, I assume you've already completed the following steps to set up the project:
- Setting up the project
- File IO in NodeJS
- A very simple web server
- NPM
- Creating custom modules
- Getting started with Express
It's also important to have a basic understanding of HTTP, which you can get by reading Overview of HTTP.
To get started, add a file named http-sample-app.js to the samples folder, and add this code to it:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
// set the 'public' folder as the location for our static files:
app.use(express.static('public'));
// allow the app to receive data from form submits
app.use(bodyParser.urlencoded({ extended: true }));
app.all('/request-handler', (req, res) => {
console.log(req.method);
res.send(parseRequest(req));
});
const parseRequest = (req) => {
let str = "";
str += "METHOD: " + req.method + "<br>";
str += "IP: " + req.ip + "<br>";
str += "PROTOCOL: " + req.protocol + "<br>";
str += "HOST: " + req.hostname + "<br>";
str += "PATH: " + req.path + "<br>"; // the path part of the URL
str += "URL: " + req.originalUrl + "<br>"; // the entire request URL
str += "SUBDOMAINS: " + JSON.stringify(req.subdomains) + "<br>"; // any subdomains in the URL
str += "QUERY STRING: " + JSON.stringify(req.query) + "<br>";
str += "BODY: " + JSON.stringify(req.body) + "<br>"; // REQUIRES bodyParser package
str += "COOKIES: " + JSON.stringify(req.cookies) + "<br>"; // REQUIRES cookie parser package
str += "FILES: " + JSON.stringify(req.files) + "<br>"; // REQUIRES express-fileUpload package
str += "<br>HEADERS<br>"
for(key in req.headers){
str += `${key}: ${req.headers[key]}<br>`;
}
return str;
}
// START THE SERVER
const port = 8080; // We'll run the server on port 8080
const server = app.listen(port, () => {
console.log("Waiting for requests on port %s", port);
});
This is an Express web application that does the following:
- Sets the public folder for static files (we'll be adding a static page later in this activity)
- Sets up the body-parser package (we'll need this so that we can access data from form submits later)
- Defines a single route (/request-handler). Note that by using the all() method for this route, it will handle all requests methods, including GET and POST requests.
- The parseRequest() function takes a Request object and parses it into a string so that we can analyze it. Here's a link to more info on the Express Request object.
Go ahead and run the application:
node samples/http-sample-app.js
Now navigate to http://localhost:8080/request-handler in your browser and you'll see a lot of the information that the browser put into the request that was sent. For now, just note that the request method is GET, but we'll explore the other details of the request later in this activity, and in the course.
Note that body of the request is empty (Express converts the body into an object, which is why you see the empty curly braces). In HTTP, GET requests do not include a body. But soon we'll send a POST request and you'll see they include a body.
Now let's add a few URL parameters by navigating to http://localhost:8080/request-handler?firstName=Bob&age=22. The part of the URL that comes after the question mark is known as the query string, and in this case it includes two parameters (firstName and age). Note that express parses this string into an object that has a firstName property and an age property.
Now we'll create a static page that includes a
Add a file named sample-form.html to the public folder of the project, and put this code into it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample Form</title>
<style>
label{
display:block;
font-weight: bold;
}
input[type=checkbox] + label, input[type=radio] + label{
display: inline;
font-weight: normal;
}
</style>
</head>
<body>
<h3>Basic Form Handling</h3>
<form method="POST" action="/request-handler">
<input type="hidden" name="hiddenInput" value="some hidden value">
<label>First name</label>
<input type="text" name="firstName">
<br>
<label>Favorite color</label>
<select name="colors">
<option>Choose a color...</option>
<option value="red">RED</option>
<option value="blue">BLUE</option>
<option value="green">GREEN</option>
</select>
<br>
<label>Status</label>
<input type="radio" value="single" name="marital-status"><label>Single</label>
<input type="radio" value="married" name="marital-status"><label>Married</label>
<br>
<label>Places you've been to:</label>
<input type="checkbox" name="places" value="canada"><label>Canada</label>
<input type="checkbox" name="places" value="us"><label>United States</label>
<input type="checkbox" name="places" value="mexico"><label>Mexico</label>
<br>
<label>Terms of service</label>
<input type="checkbox" name="agreed" value="yes"><label>Check to agree to our terms of service</label>
<br>
<input type="submit">
</form>
</html>
The code is basically just a smorgesboard of various input elements that can be used witin a form element.
Notes:
- The method attribute of the form element is set to POST, which means that when the form is submitted, it will send a POST request that includes the user input in the body of the request.
- The action attribute of the form is set to /request-handler, which is the one route that we defined in our Express app.
- You should remember from Web II that it's important to add a name attribute to each input element.
Go ahead and load the page in the browser (localhost:8080/sample-form.html).
Press the submit button and you should end up at the /request-handler route, which basically simply regurgetates details of the request so that we can analyze it.
Note that the request now includes a body, which contains the information that was entered into the form. The actual body of the request is a string that is encoded just like the URL parameters that we sent in the query string earlier. But Express will parse this string into an object.
You can see the actual string that is being sent as the body of the request if you look in the network tab of the web developer tools in your browser. I can show you this in class if you remind me.
One thing that may seem strange when handling form submits is that checkboxes and radio buttons do not get included in the request body unless they are checked. This is something that you need to be aware of when handling form submits on the server.
I added a 'hidden' input to the form. This is an input that does not appear to the user, but it's value is included when the form is submitted. Hidden inputs are often used to include IDs that have no meaning to a user, but indicate a primary key in a database.
Finally, check all the boxes for 'Places you've been to', then submit the form. You should see that Express parses the places into an array. This works because all three of the checkboxes have the same name attribute.
Normally, when a form is sumbitted, the server would do something with the data that was entered by the user. Most likely it would be inserted into a database. We'll be learning how to do this later in the program.