Express.js In Action

发布于 - 最后修改于

Express.js is a Web framework built on Node.js. In my previous articles about Node.js development I mentioned that node is a low development platform where you manually have to handle things like closing response streams, setting status codes, or writing your own methods to serve static files on your HTTP server. The express framework was created to help developers, and it has implemented a lot of convenient solutions (like serving static files or defaulting response code to HTTP 200 - Success). To render the response pages, express uses a template engine called Jade, but it is possible to define your own template engine too.

Express.js is part of the MEAN (MongoDb, Express.js, Angular.js, Node.js) stack. I will not cover installation, but you can read the Installing and Using MEAN stack article if you want more information.

Where is the code?

The source code is hosted on GitHub, in the node_projects repository and in the express_contact folder. The structure of application was created using express-generator. Find more details about how to use the express-generator on this page.

What are middlewares?

Express has the concept of middleware, which are methods within the JavaScript code that have access to the http request and http response objects, as well as a reference to the next middleware (by convention this is always called next). An express application is the mix of different middleware calls that are invoked one after another.

There are different types, including application level, router level, error handling, third party developed and built-in middleware. In this article I will show application, router and error handling middleware defined.

Using middlewares

I've implemented the error handling using application level middlewares:

app.use('/', routes);
app.use('/users', users);
app.use('/contact', contacts);

//catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

//error handlers
app.use(function(err, req, res, next) {
  if(err) {
    res.render('error', { 
        message : "Sorry, an error occured!", 
        route: req.originalUrl,
        error:err 
    });
  }
  else {
    next();
  }
});

The use() method of the app object mounts the passed function as a handler. First I mount the handlers for the root, then ones for the /users, and finally for /contact.

For handling an invalid request (meaning the application could not find a routing for the request), I create a new error with Not Found message and invoke the next() middleware passing in the error object. The code in the //error handlers part is then executed. I check if the err object has a value, then I render the error page that passes a message (Sorry, an error occurred!). I then check the requested URL path and the error object. In case there was no error, I invoke the next() method/middleware.

The application logic middlewares are registered on router level. As an example, you can find the following middleware in routes/contacts.js:

router.get('/:name', function(req, res) {
  	contacts.getContactsByName(req.params.name, function(error, data) {
  		if(error) {
  			res.render('error', { message : error });
  		}
                var tmpResult = [];
                if(Object.keys(req.query).length > 0) {
                        console.log(req.query);
                        data.forEach(function(item){
                                var tmpObj = {};
                                for(var key in req.query) {
                                       if(item.hasOwnProperty(key)) {
                                              tmpObj[key] = item[key];
                                      }
                                }
                               tmpResult.push(tmpObj);
                       });
                 }
                 else {
                       data.forEach(function(item){          
                             tmpResult.push(item);
                       });
                 }

  	res.status(200).json(tmpResult);
  	});
});

The code handles urls like /contact/John?age=1&last_name=1&first_name=1. The /contact part is where the router is registered on app level (see first code section). The /John is mapped to the /:name part of the route and the ?age=1&last_name=1&first_name=1 is the query string for the http GET request (accessible through req.query object).

Once a request comes in, getContactsByName() is invoked. If there is an error, I render the error page and finish the execution. If the data was successfully returned from the service, I use the query string (req.query) to build up JavaScript objects dynamically and add only those field values to the response that was requested in the query parameter. For the above request, the result includes age, last_name and first_name fields:

[
	{
		"age":31, 
		"last_name":"Doe", 
		"first_name":"John"
	},
	{	"age":43,
		"last_name":"Victory",
		"first_name":"John"
	}
]

If I add the &website=1 parameter to the query string the result would be:

[
	{
		"age":31, 
		"last_name":"Doe", 
		"first_name":"John",
		"website":"http://johndoe.com",
	},
	{	"age":43,
		"last_name":"Victory",
		"first_name":"John"
		"website":"http://johnvictory.com",
	}
]

If the query parameter contains invalid field names, those are ignored. Those fields won't cause any harm or cause the application to crash.

Using this code, the client application can dynamically specify what fields are needed from the full set of data. For example, if the Contact object in the backend has twenty fields, there is no need to return all the fields when the client application only requires three fields. Using this approach saves network bandwidth, and reduces serialization and response time.

发布于 5 三月, 2015

Greg Bogdan

Software Engineer, Blogger, Tech Enthusiast

I am a Software Engineer with over 7 years of experience in different domains(ERP, Financial Products and Alerting Systems). My main expertise is .NET, Java, Python and JavaScript. I like technical writing and have good experience in creating tutorials and how to technical articles. I am passionate about technology and I love what I do and I always intend to 100% fulfill the project which I am ...

下一篇文章

Post a Project via Freelancer Mobile App