Express

Express

Express 应用生成器

通过应用生成器工具 express 可以快速创建一个应用的骨架。
通过如下命令安装:

$ npm install express-generator -g

-h 选项可以列出所有可用的命令行选项:

$ express -h

      Usage: express [options] [dir]

          Options:

           -h, --help          output usage information
        -V, --version       output the version number
            -e, --ejs           add ejs engine support (defaults to jade)
        --hbs           add handlebars engine support
        -H, --hogan         add hogan.js engine support
        -c, --css <engine>  add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git           add .gitignore
        -f, --force         force on non-empty directory

例如,下面的示例就是在当前工作目录下创建一个命名为 myapp 的应用。
个命名为 myapp 的应用。

$ express myapp

       create : myapp
       create : myapp/package.json
       create : myapp/app.js
       create : myapp/public
       create : myapp/public/javascripts
       create : myapp/public/images
       create : myapp/routes
       create : myapp/routes/index.js
       create : myapp/routes/users.js
       create : myapp/public/stylesheets
       create : myapp/public/stylesheets/style.css
       create : myapp/views
       create : myapp/views/index.jade
       create : myapp/views/layout.jade
       create : myapp/views/error.jade
       create : myapp/bin
       create : myapp/bin/www

然后安装所有依赖包:

$ cd myapp 
$ npm install

启动这个应用(MacOS 或 Linux 平台):

$ DEBUG=myapp npm start

Windows 平台使用如下命令:

> set DEBUG=myapp & npm start

然后在浏览器中打开 http://localhost:3000/ 网址就可以看到这个应用了。i

通过 Express 应用生成器创建的应用一般都有如下目录结构:

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
        ├── error.jade
    ├── index.jade
    └── layout.jade

7 directories, 9 files

路由

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构如下: app.METHOD(path, [callback…], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数。

下面是一个基本的路由示例:

var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
      res.send('hello world');
});

路由方法

路由方法源于 HTTP 请求方法,和 express 实例相关联。

下面这个例子展示了为应用跟路径定义的 GET 和 POST 请求:

// GET method route
app.get('/', function (req, res) {
      res.send('GET request to the homepage');
});

// POST method route
    app.post('/', function (req, res) {
      res.send('POST request to the homepage');
});

Express 定义了如下和 HTTP 请求对应的路由方法: get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search, 和 connect。

有些路由方法名不是合规的 JavaScript 变量名,此时使用括号记法,比如: app['m-search']('/', function ...

app.all() 是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的作用是对于一个路径上的所有请求加载中间件。

在下面的例子中,来自 “/secret” 的请求,不管使用 GET、POST、PUT、DELETE 或其他任何 http 模块支持的 HTTP 请求,句柄都会得到执行。

app.all('/secret', function (req, res, next) {
      console.log('Accessing the secret section ...');
      next(); // pass control to the next handler
});

路由路径

路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。

Express 使用 path-to-regexp 匹配路由路径,请参考文档查阅所有定义路由路径的方法。 Express Route Tester 是测试基本 Express 路径的好工具,但不支持模式匹配。
查询字符串不是路由路径的一部分。

使用字符串的路由路径示例:

// 匹配根路径的请求
app.get('/', function (req, res) {
      res.send('root');
});

// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
      res.send('about');
});

// 匹配 /random.text 路径的请求
app.get('/random.text', function (req, res) {
      res.send('random.text');
});

使用字符串模式的路由路径示例:

// 匹配 acd 和 abcd
    app.get('/ab?cd', function(req, res) {
      res.send('ab?cd');
});

// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', function(req, res) {
      res.send('ab+cd');
});

// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/ab*cd', function(req, res) {
      res.send('ab*cd');
});

// 匹配 /abe 和 /abcde
app.get('/ab(cd)?e', function(req, res) {
     res.send('ab(cd)?e');
});

字符 ?、+、* 和 () 是正则表达式的子集,- 和 . 在基于字符串的路径中按照字面值解释。

使用正则表达式的路由路径示例:

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function(req, res) {
      res.send('/a/');
});

// 匹配 butterfly、dragonfly,不匹配 butterflyman、    dragonfly man等
app.get(/.*fly$/, function(req, res) {
      res.send('/.*fly$/');
});

路由句柄

可以为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next(‘route’) 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。

路由句柄有多种形式,可以是一个函数、一个函数数组,或者是两者混合,如下所示.

使用一个回调函数处理路由:

app.get('/example/a', function (req, res) {
      res.send('Hello from A!');
});

使用多个回调函数处理路由(记得指定 next 对象):

app.get('/example/b', function (req, res, next) {
      console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
      res.send('Hello from B!');
});

使用回调函数数组处理路由:

var cb0 = function (req, res, next) {
      console.log('CB0');
      next();
}

var cb1 = function (req, res, next) {
      console.log('CB1');
      next();
}

var cb2 = function (req, res) {
      res.send('Hello from C!');
}

app.get('/example/c', [cb0, cb1, cb2]);

混合使用函数和函数数组处理路由:

var cb0 = function (req, res, next) {
     console.log('CB0');
      next();
}

var cb1 = function (req, res, next) {
      console.log('CB1');
      next();
}

app.get('/example/d', [cb0, cb1], function (req, res,     next) {
     console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
      res.send('Hello from D!');
});

响应方法

下表中响应对象(res)的方法向客户端返回响应,终结请求响应的循环。如果在路由句柄中一个方法也不调用,来自客户端的请求会一直挂起。

方法 描述
res.download() 提示下载文件。
res.end() 终结响应处理流程。
res.json() 发送一个 JSON 格式的响应。
res.jsonp() 发送一个支持 JSONP 的 JSON 格式的响应。
res.redirect() 重定向请求。
res.render() 渲染视图模板。
res.send() 发送各种类型的响应。
res.sendFile 以八位字节流的形式发送文件。
res.sendStatus() 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。

app.route()

可使用 app.route() 创建路由路径的链式路由句柄。由于路径在一个地方指定,这样做有助于创建模块化的路由,而且减少了代码冗余和拼写错误。请参考 Router() 文档 了解更多有关路由的信息。

下面这个示例程序使用 app.route() 定义了链式路由句柄。

app.route('/book')
      .get(function(req, res) {
    res.send('Get a random book');
  })
  .post(function(req, res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });

express.Router

可使用 express.Router 类创建模块化、可挂载的路由句柄。Router 实例是一个完整的中间件和路由系统,因此常称其为一个 “mini-app”。

下面的实例程序创建了一个路由模块,并加载了一个中间件,定义了一些路由,并且将它们挂载至应用的路径上。

在 app 目录下创建名为 birds.js 的文件,内容如下:

var express = require('express');
var router = express.Router();

// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
      console.log('Time: ', Date.now());
  next();
});
// 定义网站主页的路由
router.get('/', function(req, res) {
      res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {
      res.send('About birds');
});

module.exports = router;

然后在应用中加载路由模块:

var birds = require('./birds');
...
app.use('/birds', birds);

应用即可处理发自 /birds 和 /birds/about 的请求,并且调用为该路由指定的 timeLog 中间件。

在 Express 中使用模板引擎

需要在应用中进行如下设置才能让 Express 渲染模板文件:

  • views, 放模板文件的目录,比如: app.set(‘views’, ‘./views’)
  • view engine, 模板引擎,比如: app.set(‘view engine’, ‘jade’)

然后安装相应的模板引擎 npm 软件包。

$ npm install jade --save

和 Express 兼容的模板引擎,比如 Jade,通过 res.render() 调用其导出方法 __express(filePath, options, callback) 渲染模板。 有一些模板引擎不遵循这种约定,Consolidate.js 能将 Node 中所有流行的模板引擎映射为这种约定,这样就可以和 Express 无缝衔接。

一旦 view engine 设置成功,就不需要显式指定引擎,或者在应用中加载模板引擎模块,Express 已经在内部加载,如下所示。

app.set('view engine', 'jade');

在 views 目录下生成名为 index.jade 的 Jade 模板文件,内容如下:

html
      head
        title!= title
      body
        h1!= message

然后创建一个路由渲染 index.jade 文件。如果没有设置 view engine,您需要指明视图文件的后缀,否则就会遗漏它。

app.get('/', function (req, res) {
      res.render('index', { title: 'Hey', message: 'Hello there!'});
});

此时向主页发送请求,“index.jade” 会被渲染为 HTML。

集成数据库

为 Express 应用添加连接数据库的能力,只需要加载相应数据库的 Node.js 驱动即可。这里将会简要介绍如何为 Express 应用添加和使用一些常用的数据库 Node 模块。

  • Cassandra
  • CouchDB
  • LevelDB
  • MySQL
  • MongoDB
  • Neo4j
  • PostgreSQL
  • Redis
  • SQLite
  • ElasticSearch

这些数据库驱动只是其中一部分,可在 npm 官网 查找更多驱动。

Cassandra

模块: cassandra-driver
安装
$ npm install cassandra-driver
示例
var cassandra = require('cassandra-driver');
var client = new cassandra.Client({ contactPoints: ['localhost']});

client.execute('select key from system.local', function(err, result) {
      if (err) throw err;
      console.log(result.rows[0]);
});

CouchDB

模块: nano
安装
$ npm install nano
示例
var nano = require('nano')('http://localhost:5984');
nano.db.create('books');
var books = nano.db.use('books');

//Insert a book document in the books database
books.insert({name: 'The Art of war'}, null, function(err, body) {
      if (!err){
        console.log(body);
      }
});

//Get a list of all books
books.list(function(err, body){
      console.log(body.rows);
}

LevelDB

模块: levelup
安装
$ npm install level levelup leveldown

######示例

var levelup = require('levelup');
var db = levelup('./mydb');

db.put('name', 'LevelUP', function (err) {

      if (err) return console.log('Ooops!', err);
          db.get('name', function (err, value) {
    if (err) return console.log('Ooops!', err);
        console.log('name=' + value)
      });

});

MySQL

模块: mysql
安装
$ npm install mysql
示例
var mysql      = require('mysql');
var connection = mysql.createConnection({
      host     : 'localhost',
      user     : 'dbuser',
      password : 's3kreee7'
});

connection.connect();

connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
      if (err) throw err;
          console.log('The solution is: ', rows[0].solution);
    });

connection.end();

MongoDB

模块: mongoskin
安装
$ npm install mongoskin
示例
var db = require('mongoskin').db('localhost:27017/animals');

db.collection('mamals').find().toArray(function(err, result) {
      if (err) throw err;
      console.log(result);
});

If you want a object model driver for MongoDB, checkout Mongoose.

Neo4j

模块: apoc
安装
$ npm install apoc
示例
var apoc = require('apoc');

apoc.query('match (n) return n').exec().then(
      function (response) {
        console.log(response);
      },
      function (fail) {
            console.log(fail);
      }
);

PostgreSQL

模块: pg
安装
$ npm install pg
示例
var pg = require('pg');
var conString = "postgres://username:password@localhost/database";

pg.connect(conString, function(err, client, done) {

      if (err) {
        return console.error('error fetching client from pool', err);
      }
      client.query('SELECT $1::int AS number', ['1'], function(err, result) {
            done();
        if (err) {
             return console.error('error running query', err);
        }
        console.log(result.rows[0].number);
      });

});

Redis

模块: redis
安装
$ npm install redis
示例
var client = require('redis').createClient();

client.on('error', function (err) {
      console.log('Error ' + err);
});

client.set('string key', 'string val', redis.print);
client.hset('hash key', 'hashtest 1', 'some value', redis.print);
client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print);

client.hkeys('hash key', function (err, replies) {

     console.log(replies.length + ' replies:');
      replies.forEach(function (reply, i) {
        console.log('    ' + i + ': ' + reply);
      });

      client.quit();

});

SQLite

模块:sqlite3
安装
$ npm install sqlite3
示例
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');

db.serialize(function() {

  db.run('CREATE TABLE lorem (info TEXT)');
  var stmt = db.prepare('INSERT INTO lorem VALUES (?)');

 for (var i = 0; i < 10; i++) {
    stmt.run('Ipsum ' + i);
 }

  stmt.finalize();

  db.each('SELECT rowid AS id, info FROM lorem', function(err,     row) {
    console.log(row.id + ': ' + row.info);
      });
});

db.close();

ElasticSearch

模块: elasticsearch
安装
$ npm install elasticsearch
示例
var elasticsearch = require('elasticsearch');
var client = elasticsearch.Client({
      host: 'localhost:9200'  
});

client.search({
      index: 'books',
      type: 'book',
      body: {
        query: {
             multi_match: {
                query: 'express js',
                fields: ['title', 'description']
              }
        }
      }
}).then(function(response) {
      var hits = response.hits.hits;
}, function(error) {
      console.trace(error.message);
});
很惭愧<br><br>只做了一点微小的工作<br>谢谢大家