19
Implementing Express Middleware

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.

Understanding Middleware

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:

Images logger: Implements a formatted request logger to track requests to the server

Images static: Allows the Express server to stream static file get requests

Images favicon: Provides functionality to support sending the favicon to the browser

Images basicAuth: Provides support for basic HTTP authentication

Images cookieParser: Allows you to read cookies from the request and set cookies in the response

Images cookieSession: Provides cookie-based session support

Images session: Provides a fairly robust session implementation

Images bodyParser: Parses the body data of POST requests into the req.body property

Images query: Converts the query string to a JavaScript object and stores it as req.query

Images compress: Provides Gzip compress support for large responses to the client

Images 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.

Assigning Middleware Globally to a Path

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());

Assigning Middleware to a Single Route

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.');
});

Adding Multiple Middleware Functions

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.

Using the query Middleware

One 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");
});

Serving Static Files

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:

Images maxAge: Sets the browser cache maxAge in milliseconds. The default is 0.

Images hidden: A Boolean that, when true, indicates that transfer of hidden files is enabled. The default is false.

Images 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.

Images 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 }
Screenshot shows a Static File window with a images of delicate arch, flower, and animals grazing over a land.

Figure 19.1 HTML, CSS, and image files served statically to a browser

Handling POST Body Data

Another 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);
Screenshot shows localhost window. A textbox labeled "First" has the value “Caleb,” and another text box labeled "Last" reads “Dayley,” with a Submit button at the bottom.
Screenshot shows a localhost window.

Figure 19.2 Handling POST parameters in the request body using the bodyParser middleware

Sending and Receiving Cookies

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:

Images maxAge: Specifies the amount of time in milliseconds for the cookie to live before it expires.

Images httpOnly: A Boolean that, when true, indicates that this cookie should only be accessed by the server and by not client-side JavaScript.

Images 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.

Images 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);

Implementing Sessions

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:

Images key: Name of the cookie that identifies the session.

Images secret: String used to sign the session cookie to prevent cookie tampering.

Images cookie: An object that defines the cookie settings, including maxAge, path, httpOnly, and signed. The default is { path:'/', httpOnly:true, maxAge:null }

Images 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);
Screenshot shows two outputs windows.

Figure 19.3 Using basic session handling to track improper access to a route

Applying Basic HTTP Authentication

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);
Screenshot shows a pop-up box in local host window.
Screenshot shows a local host restricted window. The text at the left top reads: Welcome to the restricted section.

Figure 19.4 Using basic HTTP authentication

Implementing Session Authentication

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

regenerate([callback])

Removes the req.session object and creates a new one enabling you to reset the session

destroy([callback])

Removes the req.session object

save([callback])

Saves the session data

touch([callback])

Resets the maxAge count for the session cookie

cookie

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);
Screenshot shows a localhost login window.
Screenshot shows a localhost testuser window. The screenshot shows the restricted window with the text,” Authenticated as testuser,” with the subtext, “you have entered the restricted section,” with a logout link.

Figure 19.5 Implementing session authentication in Node.js using Express session middleware

Creating Custom Middleware

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);
Screenshot of console output.

Figure 19.6 Implementing custom middleware to remove the query string from the Request object

Summary

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.

Next

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.218.226