1. Installation
$ npm install express
or use it anywhere Executable express(1) installation:
# Translation Note: This method is strongly recommended
$ npm install - g express
2. Get started quickly
The fastest way to get started with express is to use the executable express(1) to generate an application, as shown below:
Create an app:
$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
Install dependency packages:
$ npm install -d
Start the server:
$ node app.js
3. Create a server
To create an express.HTTPServer instance, just call the createServer() method. Common to this application example, we can define routing based on HTTP actions (HTTP Verbs), taking app.get() as an example:
var app = require('express').createServer();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
4. Create an HTTPS server
Initialize an express.HTTPSServer instance as above. Then we pass it a configuration object that accepts key, cert and other (properties/methods) mentioned in the https documentation.
var app = require('express').createServer( { key: ... });
5. Configuration
Express supports any environment, such as production and development. Developers can use the configure() method to set the current required environment. If the call to configure() does not include any environment names, it will run the specified callbacks in all environments.
Annotation: Aliases like production / development / stage can be chosen by yourself, as shown in app.configure in application.js. See the example below for actual usage.
The following example only dumpExceptions (throws an error) during the development phase and returns a stack exception. But in both environments we use methodOverride and bodyParser. Pay attention to the use of app.router. It can (optional) be used to load (mount) the route of the program. In addition, the first call to app.get(), app.post(), etc. will also load the route.
app.configure(function(){
app .use(express.methodOverride());
app.use(express.bodyParser());
app.use(app.router);
});
app.configure('development', function(){
app.use(express.static(__dirname '/public'));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
var oneYear = 31557600000;
app.use(express.static(__dirname '/public', { maxAge: oneYear }));
app.use(express.errorHandler());
});
For similar environments you can pass multiple environment strings:
app.configure('stage', 'prod', function(){
// config
});
For any internal setting (#), Express provides set(key[, val]), enable(key) and disable(key) methods:
Annotation: For details of settings, see: app.set in application.js.
app.configure(function(){
app .set('views', __dirname '/views');
app.set('views');
// => "/absolute/path/to/views"
app.enable('some feature');
// Equivalent to: app.set('some feature', true);
app.disable('some feature');
// Equivalent to: app.set('some feature', false);
app.enabled('some feature')
// => false
});
To change the environment, we can set the NODE_ENV environment variable, such as:
$ NODE_ENV=production node app.js
This is very important, Because most caching mechanisms are only turned on during the production phase.
6. Settings
Express supports the following out-of-the-box settings:
1.basepath is the base path of the application used for res.redirect(), which explicitly handles mounted apps.
2.view View is the default root directory For CWD/views
3.view engine The default View engine processing (View files) does not need to use the suffix
4.view cache Enable View cache (enabled in the production stage)
5.charet Change encoding, The default is utf-8
6.case sensitive routes are case sensitive in routing
7.strit routing After enabling (in routing) the trailing / will not be ignored (Annotation: app.get('/sofish ') and app.get('/sofish/') will be different)
8.json callback enables res.send() / res.json() explicit jsonp support (transparent jsonp support)
7. Routing
Express provides a set of informative and expressive routing APIs using HTTP actions. For example, if we want to process an account with the path /user/12, we can define the route as follows. Values associated with named placeholders can be accessed using req.params.
app.get('/user/:id', function(req, res){
res.send('user ' req.params.id);
});
The route is a string that is internally compiled into a regular string. For example, when /user/:id is compiled, a simplified version of the regular expression looks like this:
// Modify the official string
//user/([^/] )/?/
Regular expressions can be passed in to apply to complex scene. Since content groups captured via literal regular expressions are anonymous, we may access them directly via req.params. Therefore, the first set of content we capture will be req.params[0], while the second set is immediately following req.params[1].
app.get(/^/users?(?: /(d )(?:..(d ))?)?/, function(req, res){
res.send(req.params);
});
Curl For the request of the above defined route:
$ curl http:/ /dev:3000/user
[null,null]
$ curl http://dev:3000/users
[null,null]
$ curl http://dev:3000/users /1
["1",null]
$ curl http://dev:3000/users/1..15
["1","15"]
below They are instances of some routes, associated with the paths they may use:
"/user/:id"
/user/12
"/users/:id?"
/users/5
/users
"/files/*"
/files/jquery.js
/files/javascripts/jquery.js
"/file/*.*"
/files/jquery.js
/files/javascripts/jquery.js
"/user/:id/:operation?"
/user/1
/user/1/edit
"/products.:format"
/products.json
/products.xml
"/products.:format?"
/products.json
/products.xml
/products
"/user/:id.:format?"
/user/12
/user/12.json
For example, we can use POST to send json data through bodyParser This middleware can parse json request content (or other content) to return data and store the return result in req.body:
var express = require('express')
, app = express.createServer();
app.use(express.bodyParser());
app.post('/', function(req, res){
res.send(req.body);
});
app.listen(3000);
Usually we can use a "foolish" placeholder like user/:id that has no (naming) restrictions. However, for example, if we want to limit the user ID to only numbers, then we might use /user/:id([0-9]), which will only take effect when the placeholder contains at least one digit (adaptation , match).
8. Passing Route Control
We can control the next adapted route by calling the third parameter, the next() function. If no adapter is found, control will be passed back to Connect, and the middleware will be called in the order added in use(). The same principle applies to multiple routes defined to the same path. They will be called in sequence until one of them decides to respond to the request without calling next().
app.get('/users/:id?' , function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
app.get('/users', function(req, res){
// do something else
});
app.all() method only needs to be called once Conveniently apply the same logic to all HTTP actions. Below we use this to extract a user from the dummy data and assign it to req.user.
var express = require('express')
, app = express.createServer();
var users = [{ name: 'tj' }];
app.all('/user/:id/:op?', function(req, res, next){
req.user = users[req.params.id];
if (req .user) {
next();
} else {
next(new Error('cannot find user ' req.params.id));
}
});
app.get('/user/:id', function(req, res){
res.send('viewing ' req.user.name);
});
app.get('/user/:id/edit', function(req, res){
res.send('editing ' req.user.name);
});
app.put('/user/:id', function(req, res){
res.send('updating ' req.user.name);
});
app.get('*', function(req, res){
res.send(404, 'what???');
});
app.listen(3000);
9. Middleware
The Connect middleware (properties) used usually accompany one of your regular Connect servers, passed to express.createServer() . For example:
var express = require('express');
var app = express.createServer(
express.logger()
, express.bodyParser()
);
In addition, within the configure() block - this progressive In a progressive manner, we can also easily use use() to add middleware.
app.use(express.logger({ format: ' :method :url' }));
Usually, when using connect middleware you may use require('connect'), like this:
var connect = require('connect');
app.use(connect.logger());
app.use(connect.bodyParser());
This is somewhat annoying, so express re-exports these middleware properties, even though they are the same:
app.use(express.logger());
app.use(express.bodyParser());
The order of middleware is very important. When Connect receives a request, the first middleware we pass to createServer() or use() for execution will come with three parameters, request, response, and a callback. function (usually next). When next() is called, it will be the second middleware's turn, and so on. This is noteworthy because many middlewares depend on each other. For example, methodOverride() queries the req.body method to detect HTTP method overloads, while bodyParser() parses the request content and stores it in req. body. Another example is cookie parsing and session support, we must first use() cookieParser() followed by session().
Many Express applications contain a line like app.use(app.router). This may seem a bit strange, but in fact it is just a line that contains all the defined routing rules and performs a route lookup based on the existing URL request and HTTP method. A middleware function. Express allows you to determine its to position, but by default it is placed at the bottom. By changing the location of the route, we can change the priority of the middleware. For example, we want to make error reporting the last middleware so that any exceptions passed to next() can be handled through it; or we want static files The service priority is lower to allow our router to listen for the number of downloads of a single static file request, etc. This looks more or less like this:
app.use( express.logger(...));
app.use(express.bodyParser(...));
app.use(express.cookieParser(...));
app.use (express.session(...));
app.use(app.router);
app.use(express.static(...));
app.use(express.errorHandler (...));
First we add logger(), which may contain the node's req.end() method to provide us with response time data. The content of the next request will be parsed (if there is data), followed by cookie parsing and session support. At the same time, req.session will be defined when the route in app.router is triggered. We do not call it at this time. next(), so the static() middleware will not know this request. If the following route has been defined, we can record various statuses, refuse downloads, consume download points, etc.
var downloads = {};
app.use(app.router);
app.use(express.static(__dirname '/public'));
app.get('/*', function(req, res, next){
var file = req.params[0];
downloads[file] = downloads[file] || 0;
downloads[file] ;
next();
});
10. Routing middleware
Routing can use router middleware to pass more than one callback function (or array) to its method. This feature is great for restricting access, downloading data via routing, etc.
Usually asynchronous data retrieval might look like the following example, where we use the :id parameter and try to load a user:
app.get('/user/:id', function(req, res, next){
loadUser(req.params.id, function(err, user ){
if (err) return next(err);
res.send('Viewing user ' user.name);
});
});
is To ensure the DRY principle and improve readability, we can apply this logic within a middleware. As shown below, abstracting this logic into middleware will allow you to reuse it while keeping our routing simple.
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user ) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' req.user.name);
});
Multiple routes can be applied sequentially to deeper logic, such as restricting access to a user account. The example below allows only authenticated users to edit his or her account.
function andRestrictToSelf(req, res, next) {
req.authenticatedUser.id == req.user.id
? next()
: next(new Error('Unauthorized'));
}
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' req.user.name);
} );
Always remember that routes are just simple functions, as shown below, we can define functions that return middleware to create a more expressive and flexible solution.
function andRestrictTo(role) {
return function( req, res, next) {
req.authenticatedUser.role == role
? next()
: next(new Error('Unauthorized'));
}
}
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' req.user.name);
});
Commonly used middleware "stacks" can be passed through an array (will be applied recursively), which can be mixed and matched to any level (which can be mixed and matched to any degree).
var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
, all = [a, b];
app.get('/foo', a, function(){});
app.get('/bar', a, function(){});
app.get('/', a, middleware3, middleware4, function(){});
app.get('/', a, b, function(){});
app .get('/', all, function(){});
For the complete code of this example, please see the route middleware example repository.
We may want to "skip" the remaining routing middleware many times and continue to match subsequent routes. To do this, we simply call next() with the 'route' string - next('route'). If no remaining routes match the requested URL, Express will return 404 Not Found.
11. HTTP method
I have come across app.get() several times so far. In addition, Express also provides other common HTTP actions, such as app.post(), app.del(), etc.
A common example of POST usage is to submit a form. Next we simply set the method attribute of the form to post in html, and control will be assigned to the route defined below.
By default Express does not know how to handle the content of this request, so we must add bodyParser middleware , it will parse the content requested by application/x-www-form-urlencoded and application/json, and store the variables in req.body. We can use this middleware like the following example:
app.use(express.bodyParser());
As shown below, our route will have access to the req.body.user object, which will contain name and email when defined. (Annotation: If the content sent by the form is not empty).
app.post('/', function(req, res){
console.log(req.body.user);
res.redirect('back');
});
When you want to use something like PUT in a form In this way, we can use a hidden input named _method, which can be used to modify the HTTP method. In order to do this, we first need the methodOverride middleware, which must appear after the bodyParser in order to use the form values contained in its req.body.
app.use(express.bodyParser());
app.use(express.methodOverride());
As for why these methods are not available by default, simply because it is not required for the complete functionality required by Express. The use of methods depends on your application. You may not need them. The client can still use methods like PUT and DELETE. You can use them directly because methodOverride provides a very good solution for forms. The following will demonstrate how to use the PUT method, which may look like:
app.put('/', function(){
console.log(req.body.user);
res.redirect('back');
});
12. Error handling
Express provides the app.error() method so that the received exception can be thrown in a route or passed to next(err). The following example will handle different pages based on a specific NotFound exception:
function NotFound(msg){
this.name = 'NotFound';
Error.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
}
NotFound.prototype.__proto__ = Error.prototype;
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res){
throw new Error('keyboard cat!');
});
As follows, we can Calling app.error() multiple times. Here we detect instances of NotFound and either display a 404 page or pass it to the next error handler. It is worth noting that these handlers can be defined anywhere, as they will be placed under the route handler during listen(). It allows definitions within the configure() block so that we can use different exception handling methods based on the environment.
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
} else {
next(err);
}
}) ;
For the sake of simplicity, here we assume that all errors in this demo are 500. Of course, you can choose the one you like. When node executes a system call on the file system, you may receive an error.code with ENOENT, which means "no such file or directory exists" error. We can use it in the error handler, or when there is A specified page can be displayed when needed.
app.error(function(err, req, res) {
res.render('500.jade', {
error: err
});
});
Our app can also use Connect's errorHandler middleware to report exceptions. For example, when we want to output stderr exceptions in the "development" environment, we can use:
app.use(express.errorHandler({ dumpExceptions: true }));
At the same time during the development phase we may need to display the exceptions we pass and throw on fancy HTML pages. For this we can set showStack to true.
app.use(express.errorHandler({ showStack: true , dumpExceptions: true }));
errorHandler middleware can also return json when Accept: application/json exists, which is very useful for developing applications that rely heavily on client-side Javascript.
13. Route parameter preprocessing
Route parameter preprocessing, through implicit data loading and request verification, can greatly improve the readability of your program. For example, you often need to continuously obtain basic data from multiple routes. Like loading a user with /user/:id, usually we might do it like this:
app.get('/user/:userId', function(req, res, next){
User.get(req.params.userId, function(err, user){
if (err) return next(err);
res.send('user ' user.name);
});
});
Through preprocessing, we Parameters can be mapped to callbacks that perform validation, control, or even load data from the database. As follows, we call app.param() with the parameter name, hoping to map it to some middleware. As you can see, we accept the id parameter which represents the placeholder value. Using this, we load the user and handle errors as normal, and simply call next() to pass control to the next preprocessor or route handler.
app.param('userId', function(req, res, next, id){
User.get(id, function(err, user){
if (err) return next(err);
if (!user) return next(new Error( 'failed to find user'));
req.user = user;
next();
});
});
Once this is done, the above Will greatly improve the readability of routing and allow us to easily share logic throughout the program:
app.get('/user/:userId', function(req, res){
res.send('user ' req.user.name);
});
14. View processing
View files use the format ., where is the name of the required module. For example layout.ejs will tell the view system to require('ejs'), the loaded module must (export) the exports.compile(str, options) method, and return a Function to accommodate Express. app.register() can be used to change this default behavior and map file extensions to a specific engine. For example "foo.html" can be processed by ejs.
The following example uses Jade to process index.html. Because we are not using layout: false, the content processed by index.jade will be passed into a local variable named body in layout.jade.
app.get('/', function(req, res){
res.render('index.jade', { title: 'My Site' });
});
The new view engine setting allows us to specify the default template engine , for example, when we use jade, we can set it like this:
app .set('view engine', 'jade');
allows us to do this:
res.render('index');
corresponds to:
res.render('index.jade');
When the view engine is set, the extension is optional, but we can still mix and match Template engine:
res.render('another-page. ejs');
Express also provides view options settings, which will be applied every time a view is rendered. For example, you may do this if you do not want to use layouts:
app.set('view options', {
layout: false
});
This can be overloaded inside the res.render() call when needed:
res.render('myview.ejs', { layout: true });
When there is a need to change a layout, we Usually you need to specify another path. For example, when we have set the view engine to jade, and the file is named ./views/mylayout.jade, we can simply pass the parameters like this:
res.render('page', { layout: 'mylayout' });
Otherwise (Annotation: No When the view engine is set to jade or other engines), we must specify an extension:
res.render('page', { layout: 'mylayout.jade' });
They can also be absolute paths:
res.render('page', { layout: __dirname '/../../mylayout.jade ' });
There is a good example for this - customizing the opening and closing tags of ejs:
app.set('view options', {
open: '{{',
close: '}}'
})
15. View component
Express’s view system has built-in support for parts and collections, which is equivalent to replacing a document fragment with a “mini” view. For example, to display comments repeatedly in a view, we can use the widget set:
partial('comment', { collection: comments });
If no other options or local variables are needed, we can omit the entire object and simply pass in an array, which is the same as The above are equivalent:
partial('comment', comments);
In use, the widget set provides support for some "magic" local variables for free:
1.firstInCollection true, when it is the first object
2.indexInCollection index in the collector object
3.lastInCollection true, when it is the last object
4 .collectionLength The length of the collector object
The transfer (generation) of local variables has a higher priority. At the same time, local variables passed to the parent view are also adapted to the child view. For example, when we use partial('blog/post', post) to render a blog post, it will generate a post local variable. There is a local variable user in the view that calls this function, and it will also be valid for blog/post. (Annotation: partial here is more like the include method in php).
Note: Please use widget collectors with caution. Rendering a widget collection array with a length of 100 is equivalent to us needing to process 100 views. For simple collections, it is better to repeat the built-in rather than use a widget collector to avoid excessive overhead.
16. View Search
View search is performed relative to the parent view (path). For example, if we have a view page called views/user/list.jade, and partial('edit') is written inside it, it will try to load views/ user/edit.jade, similarly partial('../messages') will load views/messages.jade.
The View system also supports template indexing, allowing you to use a directory with the same name as the view. For example, in a route, res.render('users') returns views/users.jade, which is views/users/index.jade. (Annotation: First handle the . case, and then handle the / case. For details, see view.js.)
When using the above view index, we use partial('users') to reference views/users/index.jade in the same directory as the view. At the same time, the view system will try to index ../users/index. Without us calling partial('users').
17. Template Engines
The following are the most commonly used template engines for Express:
1.Haml: haml implementation
2.Jade: haml.js successor
3.EJS: Embedded JavaScript
4.CoffeeKup: Templates based on CoffeeScript
5.jQuery Templates
18. Session support
Session support can be obtained by using Connect's session middleware. For this, we usually need to add the cookieParser middleware in front of it, which will parse and store cookie data in req.cookies.
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));
By default, the session middleware uses Connect's built-in memory storage, but there are many other implementations. For example, connect-redis provides a Redis session storage, which can be used as follows:
var RedisStore = require('connect-redis')(express);
app.use(express.cookieParser());
app.use(express.session( { secret: "keyboard cat", store: new RedisStore }));
At this point, the req.session and req.sessionStore properties will be available to all routes and subsequent middleware. All properties on req.session will be automatically saved in a response, for example when we want to add data to the shopping cart:
var RedisStore = require('connect-redis')(express);
app.use(express.bodyParser());
app .use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
app.post('/add-to-cart', function(req, res){
// We may POST multiple items through a form
// (use bodyParser() in some cases Middleware)
var items = req.body.items;
req.session.items = items;
res.redirect('back');
});
app.get('/add-to-cart', function(req, res){
// When returned, the page GET /add-to-cart
// We can check req. session.items && req.session.items.length
// To print out the prompt
if (req.session.items && req.session.items.length) {
req.notify('info' , 'You have %s items in your cart', req.session.items.length);
}
res.render('shopping-cart');
});
For req.session, it also has methods like Session#touch(), Session#destroy(), Session#regenerate(), etc. to maintain and operate the session. See the Connect Session documentation for more details.
19. Upgrade Guide
For students using Express 1.x, if you have important programs that need to be upgraded to 2.x for better support, please see the official very detailed migration guide : http:/ /expressjs.com/guide.html#migration-guide