Much of the functionality that Express brings to the table is through middleware functions that get executed between the point when the request is received by Node.js and the time that the response is sent. Express uses the connect
module to provide a middleware framework that allows you to easily insert middleware functionality on a global or path level or for a single route.
The middleware provided with Express allows you to quickly support serving static files, implement cookies, support sessions, process POST
data, and much more. You can even create your own custom middleware functions that can be used to preprocess the requests and provide your own functionality.
This chapter focuses on the basics of implementing Express middleware. It also provides some examples of using middleware to handle POST
requests, serve static files, and implement sessions, cookies, and authentication.
Express provides a simple but effective middleware framework that allows you to provide additional functionality between the point when a request is received and when you actually handle the request and send the response. Middleware allows you to apply authentication, cookies, and sessions and otherwise manipulate the request before it is passed to the handler.
Express is built on top of the connect
NPM module, which provides the underlying middleware support. The following list describes some of the built-in middleware components that come with Express. Additional Express middleware components are available as NPMs if you query the NPM repository, and you can also create your own custom middleware:
logger
: Implements a formatted request logger to track requests to the server
static
: Allows the Express server to stream static file get requests
favicon
: Provides functionality to support sending the favicon to the browser
basicAuth
: Provides support for basic HTTP authentication
cookieParser
: Allows you to read cookies from the request and set cookies in the response
cookieSession
: Provides cookie-based session support
session
: Provides a fairly robust session implementation
bodyParser
: Parses the body data of POST
requests into the req.body
property
query
: Converts the query string to a JavaScript object and stores it as req.query
compress
: Provides Gzip compress support for large responses to the client
csrf
: Provides cross-site request forgery protection
Middleware can be applied either globally to all routes under a specific path or to specific routes. The following sections describe each of these methods.
To assign middleware to all routes, you can implement the use()
method on the Express application
object. The use()
method uses the following syntax:
use([path], middleware)
The path
variable is optional and defaults to /, which mean all paths. The middleware
is a function that has the following syntax, where req
is the Request
object, res
is the Response
object, and next
is the next middleware function to execute:
function(req, res, next)
Each of the built-in middleware components has a constructor that returns the appropriate middleware function. For example, to apply the logger
middleware to all paths with default parameters, you use the following statements:
var express = require('express'); var app = express(); app.use('/', express.logger());
You can also apply logger
to a single route by passing it after the path
parameter. For example, in the following code, requests to the /loggedRoute
are logged; however, requests to the /otherRoute
are not logged.
app.get('/loggedRoute', express.logger(), function(req, res) { res.send('This request was logged.'); }); app.get('/otherRoute', function(req, res) { res.send('This request was not logged.'); });
You can assign as many middleware functions globally and to routes as you want. For example, the following code assigns the query
, logger
, and bodyParser
middleware modules:
app.use('/', express.logger()). use('/', express.query()). use('/', express.bodyParser());
Keep in mind that the order you assign the functions is the order that they will be applied during a request. Some middleware functions need to be added before others.
query
MiddlewareOne of the most useful and simple middleware components is the query
middleware. The query
middleware converts the query string in the URL into a JavaScript object and stores it as the query
property on the Request
object.
The following code shows the basics of implementing the query
middleware. The query string for the request looks like ?id=10,score=95
. Notice that JSON.stringify
can be called on req.query
because it is a JavaScript object.
var express = require('express'); var app = express(); app.use('/', express.query()); app.get('/', function(req, res) { var id = req.query.id; var score = req.query.score; console.log(JSON.stringify(req.query)); res.send("done"); });
A commonly used Express middleware is the static
middleware, which allows you to serve static files directly from disk to the client. You can use static
middleware to support things like JavaScript files, CSS files, image files, and HTML documents that do not change. The static
module is easy to implement and uses the following syntax:
express.static(path, [options])
The path
is the root path to where the static files are referenced from in the requests. The options
parameter allows you to set the following properties:
maxAge
: Sets the browser cache maxAge
in milliseconds. The default is 0
.
hidden
: A Boolean that, when true
, indicates that transfer of hidden files is enabled. The default is false
.
redirect
: A Boolean that, when true
, indicates that if the request path is a directory, the request is redirected to the path with a trailing /. The default is true
.
index
: Specifies the default filename for the root path. The default is index.html
.
Listings 19.1 through 19.3 show the Express code, HTML, and CSS that illustrate implementing the static
middleware to support serving a static HTML, CSS, and image file. Notice that two static
paths are implemented: one for the route /
that maps to a subdirectory named static
and the second for route /images
that maps to a peer directory named images
. Figure 19.1 shows the statically served HTML document in a web browser.
Listing 19.1 express_static.js
: Express code that implements two static routes
1 var express = require('express'); 2 var app = express(); 3 app.use('/', express.static('./static'), {maxAge:60*60*1000}); 4 app.use('/images', express.static( '../images')); 5 app.listen(80);
Listing 19.2 ./static/index.html
: Static HTML file that requests the CSS and image files from the server
01 <html> 02 <head> 03 <title>Static File</title> 04 <link rel="stylesheet" type="text/css" href="css/static.css"> 05 </head> 06 <body> 07 <img src="/images/arch.jpg" height="200px"/> 08 <img src="/images/flower.jpg" height="200px" /> 09 <img src="/images/bison.jpg" height="200px" /> 10 </body> 11 </html>
Listing 19.3 ./static/css/static.css
: CSS file that formats the images
1 img 2 { 3 display:inline; 4 margin:3px; 5 border:5px solid #000000; 6 }
POST
Body DataAnother common use for Express middleware is to handle body data inside a POST
request. The data inside a request body can be in various formats such as POST
parameter strings, JSON strings, or raw data. Express provides the bodyParser
middleware that attempts to parse the data in the body of requests and properly format them as the req.body
property of the Request
object.
For example, if POST
parameters or JSON data is received they are converted to a JavaScript object and stored as the req.body
property of the Request
object. Listing 19.4 illustrates using the bodyParser
middleware to support reading form data posted to the server.
Lines 4–9 handle the GET
request and respond with a basic form. It is not well formatted HTML; however, it is adequate to illustrate the use of the bodyParser
middleware.
Lines 11–20 implement a POST
request handler. Notice that in line 16, the first name entered in the form field is accessed using req.body.first
to help build the hello message in the response. That really is it. You can handle any kind of form data in the body in this manner. Figure 19.2 shows the web form usage in the browser.
Listing 19.4 express_post.js
: Handling POST
parameters in the request body using the bodyParser
middleware
01 var express = require('express'); 02 var app = express(); 03 app.use(express.bodyParser()); 04 app.get('/', function (req, res) { 05 var response = '<form method="POST">' + 06 'First: <input type="text" name="first"><br>' + 07 'Last: <input type="text" name="last"><br>' + 08 '<input type="submit" value="Submit"></form>'; 09 res.send(response); 10 }); 11 app.post('/',function(req, res){ 12 var response = '<form method="POST">' + 13 'First: <input type="text" name="first"><br>' + 14 'Last: <input type="text" name="last"><br>' + 15 '<input type="submit" value="Submit"></form>' + 16 '<h1>Hello ' + req.body.first + '</h1>'; 17 res.type('html'); 18 res.end(response); 19 console.log(req.body); 20 }); 21 app.listen(80);
Figure 19.2 Handling POST
parameters in the request body using the bodyParser
middleware
The cookieParser
middleware provided in Express makes handling cookies simple. The cookieParser
middleware parses the cookies from the request and stores them in the req.cookies
property as a JavaScript object. The cookieParser
middleware uses the following syntax:
express.cookieParser([secret])
The optional secret
string parameter prevents cookie tampering by internally signing the cookies using the secret string.
To set a cookie in a response, you can use the res.cookie()
method shown below:
res.cookie(name, value, [options])
A cookie with the name
and value
specified is added to the response. The options
property allows you to set the following properties for the cookie:
maxAge
: Specifies the amount of time in milliseconds for the cookie to live before it expires.
httpOnly
: A Boolean that, when true
, indicates that this cookie should only be accessed by the server and by not client-side JavaScript.
signed
: A Boolean that, when true
, indicates that the cookie will be signed and you need to access it using the req.signedCookie
object instead of the req.cookie
object.
path
: Specifies the path that the cookie applies to.
For example, the following sets a hasVisited
cookie:
res.cookie('hasVisited', '1', { maxAge: 60*60*1000, httpOnly: true, path:'/'});
Cookies can be removed from the client using the res.clearCookie()
method. For example:
res.clearCookie('hasVisited');
Listing 19.5 illustrates a simple implementation of getting a cookie named req.cookies.hasVisited
from the request, and if it hasn’t been set, setting it.
Listing 19.5 express_cookies.js
: Sending and receiving cookies using Express
01 var express = require('express'); 02 var app = express(); 03 app.use(express.cookies()); 04 app.get('/', function(req, res) { 05 console.log(req.cookies); 06 if (!req.cookies.hasVisited){ 07 res.cookie('hasVisited', '1', 08 { maxAge: 60*60*1000, 09 httpOnly: true, 10 path:'/'}); 11 } 12 res.send("Sending Cookie"); 13 }); 14 app.listen(80);
You can also use Express middleware to provide session support for your applications. For complex session management, you may want to implement it yourself; however, for basic session support, the cookieSession
middleware works relatively well.
The cookieSession
middleware utilizes the cookieParser
middleware underneath, so you need to add cookieParser
prior to adding cookieSession
. The following shows the syntax for adding the cookieSession
middleware:
res.cookie([options])
The options
property allows you to set the following properties for the cookie:
key
: Name of the cookie that identifies the session.
secret
: String used to sign the session cookie to prevent cookie tampering.
cookie
: An object that defines the cookie settings, including maxAge
, path
, httpOnly
, and signed
. The default is { path:'/', httpOnly:true, maxAge:null }
proxy
: A Boolean that, when true
, causes Express to trust the reverse proxy when setting secure cookies via x-forwarded-proto
.
When cookieSession
is implemented, the session is stored as an object in req.session
. Any changes you make to req.session
flow across multiple requests from the same browser.
Listing 19.6 shows an example of implementing a basic cookieSession
session. Notice that cookieParser
is added first in line 3 and then cookieSession
is added in line 4 with a secret string. There are two routes in the example. When /restricted
route is accessed, the restrictedCount
value is incremented in the session and the response is redirected to /library
. Then in library
, if the restrictedCount
is not undefined
, the value is displayed; otherwise, a welcome message is displayed. Figure 19.3 shows the different outputs in the web browser.
Listing 19.6 express_session.js
: Implementing a basic cookie session using Express
01 var express = require('express'); 02 var app = express(); 03 app.use(express.cookieParser()); 04 app.use(express.cookieSession({secret: 'MAGICALEXPRESSKEY'})); 05 app.get('/library', function(req, res) { 06 console.log(req.cookies); 07 if(req.session.restricted) { 08 res.send('You have been in the restricted section ' + 09 req.session.restrictedCount + ' times.'); 10 }else { 11 res.send('Welcome to the library.'); 12 } 13 }); 14 app.get('/restricted', function(req, res) { 15 req.session.restricted = true; 16 if(!req.session.restrictedCount){ 17 req.session.restrictedCount = 1; 18 } else { 19 req.session.restrictedCount += 1; 20 } 21 res.redirect('/library'); 22 }); 23 app.listen(80);
Figure 19.3 Using basic session handling to track improper access to a route
Express middleware also is commonly used to apply basic HTTP authentication. HTTP authentication uses the Authorization
header to send the encoded username and password from the browser to the server. If no authorization information is stored in the browser for the URL, the browser launches a basic login dialog box to allow the user to enter the username and password. Basic HTTP authentication works well for basic sites that require a minimal authentication method, and is easy to implement.
The basicAuth
middleware function in Express provides the support to handle basic HTTP authentication. The basicAuth
middleware uses the following syntax:
express.basicAuth(function(user, pass){})
The function passed to basicAuth
accepts the user
and password
and then returns true
if they are correct or false
if they are not. For example:
app.use(express.basicAuth(function(user, password) { return (user === 'testuser' && pass === 'test'); }));
Typically, you store the user
and password
in the database and inside the authentication function retrieve the user object to validate against.
Listing 19.7 and Listing 19.8 illustrate how easy it is to implement the basicAuth
middleware. Listing 19.7 implements a global authentication, and Listing 19.8 implements authentication against a single route. Figure 19.4 shows the browser requesting authentication and then the authenticated webpage.
Listing 19.7 express_auth.js
: Implementing basic HTTP authentication globally for the site
1 var express = require('express'); 2 var app = express(); 3 app.listen(80); 4 app.use(express.basicAuth(function(user, pass) { 5 return (user === 'testuser' && pass === 'test'); 6 })); 7 app.get('/', function(req, res) { 8 res.send('Successful Authentication!'); 9 });
Listing 19.8 express_auth_one.js
:
Implementing basic HTTP authentication for a single route
01 var express = require('express'); 02 var app = express(); 03 var auth = express.basicAuth(function(user, pass) { 04 return (user === 'testuser' && pass === 'test'); 05 }); 06 app.get('/library', function(req, res) { 07 res.send('Welcome to the library.'); 08 }); 09 app.get('/restricted', auth, function(req, res) { 10 res.send('Welcome to the restricted section.'); 11 }); 12 app.listen(80);
A major downside to basic HTTP authentication is that the login sticks around as long as the credentials are stored and is not really all that secure. A much better route is to implement your own authentication and store the authentication in a session that you can expire whenever you want.
The session
middleware inside Express works well for implementing session authentication. The session
middleware attaches a Session
object req.session
to the Request
object that provides the session functionality. Table 19.1 describes the methods that can be called on the res.session
object.
Table 19.1 Methods on the res.session
object to manage sessions
Option |
Description |
|
Removes the |
|
Removes the |
|
Saves the session data |
|
Resets the |
|
Specifies the cookie object linking the session to the browser |
Listing 19.9 illustrates implementing session authentication using the crypto
module to generate secure passwords. The example is rudimentary to keep it small enough for the book; however, it contains the basic functionality so you can see how to implement session authentication.
The passwords are encrypted using the hasPW()
function in lines 3–6. Notice that the bodyParser
, cookieParser
, and session
middleware are used. Lines 41 and 42 simulate getting a user object from the database and comparing the stored password hash with the password hash from the request body. The session
is created in lines 45–49. Notice that the regenerate()
function is used to regenerate a new session
and that the callback
function passed to regenerate()
sets the session.user
and session.success
properties of the session. If the authentication fails, only the session.error
property is set for the session
.
The /login
get route in lines 26–38 displays a rudimentary login to get credentials. If session.error
is set, it is also displayed on the login page. The /restricted
route in lines 11–20 checks the session to see whether it has a valid user, and if so displays a success message; otherwise, the session.error
is set and the response is redirected to /login
.
The /logout
route in lines 21–25 calls destroy()
on the session
to remove the authentication. You could also have other code destroy the session
based on a timeout, amount of requests, and so on. Figure 19.5 shows the browser screens forcing a login and then displaying success.
Listing 19.9 express_auth_session.js
: Implementing session authentication in Express
01 var express = require('express'); 02 var crypto = require('crypto'); 03 function hashPW(pwd){ 04 return crypto.createHash('sha256').update(pwd). 05 digest('base64').toString(); 06 } 07 var app = express(); 08 app.use(express.bodyParser()); 09 app.use(express.cookieParser('MAGICString')); 10 app.use(express.session()); 11 app.get('/restricted', function(req, res){ 12 if (req.session.user) { 13 res.send('<h2>'+ req.session.success + '</h2>' + 14 '<p>You have Entered the restricted section<p><br>' + 15 ' <a href="/logout">logout</a>'); 16 } else { 17 req.session.error = 'Access denied!'; 18 res.redirect('/login'); 19 } 20 }); 21 app.get('/logout', function(req, res){ 22 req.session.destroy(function(){ 23 res.redirect('/login'); 24 }); 25 }); 26 app.get('/login', function(req, res){ 27 var response = '<form method="POST">' + 28 'Username: <input type="text" name="username"><br>' + 29 'Password: <input type="password" name="password"><br>' + 30 '<input type="submit" value="Submit"></form>'; 31 if(req.session.user){ 32 res.redirect('/restricted'); 33 }else if(req.session.error){ 34 response +='<h2>' + req.session.error + '<h2>'; 35 } 36 res.type('html'); 37 res.send(response); 38 }); 39 app.post('/login', function(req, res){ 40 //user should be a lookup of req.body.username in database 41 var user = {name:req.body.username, password:hashPW("myPass")}; 42 if (user.password === hashPW(req.body.password.toString())) { 43 req.session.regenerate(function(){ 44 req.session.user = user; 45 req.session.success = 'Authenticated as ' + user.name; 46 res.redirect('/restricted'); 47 }); 48 } else { 49 req.session.regenerate(function(){ 50 req.session.error = 'Authentication failed.'; 51 res.redirect('/restricted'); 52 }); 53 res.redirect('/login'); 54 } 55 }); 56 app.listen(80);
A great feature of Express middleware is the ability to create your own. All you need to do is provide a function that accepts the Request
object as the first parameter, the Response
object as the second parameter, and a next
parameter as the third. The next
parameter is a function passed by the middleware framework that points to the next middleware function to execute, so you must call next()
prior to exiting your custom function or the handler will never be called.
To illustrate how easy it is to implement your own custom middleware functionality in Express, Listing 19.10 implements a middleware function named queryRemover()
that strips the query string off the URL prior to sending it on to the handler.
Notice that queryRemover()
accepts a the Request
and Response
objects as the first two parameters and the next
parameter as the third. The next()
callback function is executed prior to leaving the middleware function as required. Figure 19.6 displays the console output showing that the query string portion of the URL has been removed.
Listing 19.10 express_middleware.js
: Implementing custom middleware to remove the query string from the Request
object
01 var express = require('express'); 02 var app = express(); 03 function queryRemover(req, res, next){ 04 console.log(" Before URL: "); 05 console.log(req.url); 06 req.url = req.url.split('?')[0]; 07 console.log(" After URL: "); 08 console.log(req.url); 09 next(); 10 }; 11 app.use(queryRemover); 12 app.get('/no/query', function(req, res) { 13 res.send("test"); 14 }); 15 app.listen(80);
Figure 19.6 Implementing custom middleware to remove the query string from the Request
object
This chapter introduced you to the world of Express middleware and how to implement middleware in your code. The parseBody
middleware allows you to parse POST
parameters or JSON data in the body of the request. The static
middleware allows you to set routes to serve static files such as JavaScript, CSS, and images. The cookieParser
, cookieSession
, and session
middleware allow you to implement cookies and sessions.
You also learned how to use the middleware framework to implement basic HTTP authentication and more advanced session authentication. A great advantage of Express middleware is that it is simple to implement your own middleware functionality.
In the next chapter, you jump into the world of Angular and get an overview of the TypeScript language to prepare to build Angular components. You learn where Angular fits in the Node.js stack and how to begin implementing it in your projects.
18.188.218.226