Express学习之指南<1>

一.路由

路由是对一个应用的终点(即URI)以及如何回应客户端请求的定义。

路由是由一个URI,一个HTTP请求方法(GETPOST等)与一个或多个终点的处理器的结合。

路由以下面的格式进行定义:app.METHOD(PATH, HANDLER),来解释下这几个名词的意思:

  • app:是Express应用的实例
  • METHOD:指的是HTTP请求的方法(包括GET、POST等)
  • PATH:是服务器上的一个路径
  • HANDLER:这是一个函数,当客户端请求的URL搭配路由的时候,就会执行这个函数。

下面是一个非常基础的路由例子:

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

//当对主页发送一个GET请求时,就会回复"hello world"
app.get('/', function(req, res) {
  res.send('hello world');
});

1.路由方法

路由方法是来源于HTTP方法,隶属于express实例对象。

以下是一个用GETPOST方法定义路由的例子,对应用的根路径进行请求。

// GET 方法的路由
app.get('/', function (req, res) {
  res.send('GET request to the homepage');
});

// POST 方法的路由
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, and connect

注意:

对于那些被看作是无效的JavaScript变量名的路由方法,我们要使用方括号。例如,app['m-search']('/', function ...);

有一个特殊的路由方法,app.all(),它并非来源于HTTP的方法。它被用来加载一个路径中的中间件,对于所有类型的请求方法。

在下面的例子中,无论是使用GET, POST, PUT, DELETE,HTTP方法,还是使用在http模块中其它任何HTTP方法来对 “/secret”路径发送请求,处理器总会被执行。

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

2.路由路径

路由路径,与请求方法相结合,定义了可以发送请求的网络路径。路由路径可以是字符串,字符串模式,或者正则表达式。

Express使用 path-to-regexp(路径转化为正则表达式)来搭配路由路径。在它的文档中,你可以看到定义路由路径的所有可能性。 Express Route Tester 是一个测试Express基本的路由路径的便利工具,尽管它不支持模式匹配。

<1>路由路径是字符串:

// 搭配对根路径的请求
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');
});

<2>.路由路径是字符串模式:

// 搭配"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');
});

<3>路由路径是正则表达式:

// 搭配包含`a`的任何路径
app.get(/a/, function(req, res) {
  res.send('/a/');
});

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

3.路由处理器

你可以提供多个回调函数,就像中间件那样来处理一个请求。唯一的异常就是这些回调函数可能会执行next('route')来避开还未执行的回调函数。你可以使用这种机制对一个路由强制加一个先决条件,如果你不想继续处理当前路由的话,可以将控制权交给接下来的路由。

路由处理器可以是一个函数,函数的数组,或者二者结合。如下面的例子:

<1>使用一个单独的回调函数来处理路由:

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

<2>使用多个回调函数来处理路由(确保声明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!');
});

<3>使用函数数组来处理路由:

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

<4>使用回调函数数组和一个单独的回调函数来处理路由:

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

4.响应方法

以下表中response(res)对象中的方法可以向客户端发送一个响应,中断请求、响应循环。如果路由处理器不调用任何方法的话,客户端将会处于挂起状态。

  • res.download():提供一个下载的文件
  • res.end():结束响应进程
  • res.json():发送一个JSON响应
  • res.jsonp():发送一个支持JSONP的JSON响应
  • res.redirect():重定向一个请求
  • res.render():渲染一个视图模版
  • res.send():发送一个包含各种类型数据的响应
  • res.sendFile:发送一个数据流
  • res.sendStatus():设置响应状态码,将它转化为字符串作为响应体发送出去。

5.app.route()

一个路由路径的链式路由处理器可以通过app.route()方法来创建,因为路径处在一个单独的位置,它有助于创建模块化的路由和减少多余性、错误性。想要了解更多关于路由的知识,请看 Router()文档

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

6.express.Router

express.Router这个类可以被用来创建模块化的路由处理器。Router实例是一个完整的中间件和路由系统,因为这个,它被称为一个“mini-app”。

以下的例子,将一个Router创建成一个模块,在模块中加载了一个中间件,定义了一个路由,并将它设置在应用的一个路径上。

在应用目录中创建一个router文件birds.js,内容如下:

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

// 这个router的中间件
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});
// define the home page route
router.get('/', function(req, res) {
  res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
  res.send('About birds');
});

module.exports = router;

然后,在应用中加载这个router模块:

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

现在,应用能够处理对/birds/birds/about发送的请求,还能对于特定的路由调用timeLog中间件。

二.使用中间件

Express这个web框架的特色就是routing(路由)与midware(中间件),自身的功能比较少。Express应用的本质是一系列中间件的调用。

中间件相当于一个函数,通过它,可以获得request对象 (req)response对象 (res),以及在应用的request-response循环中的下一个中间件(通常用next变量名表示)。

中间件可以做:

  • 执行任意代码
  • requestresponse对象修改
  • 结束request-response循环
  • 调用栈中的其它中间件

如果当前的中间件没有结束request-response循环的话,它必须要调用next()将控制器交给下一个中间件,否则请求就会处于挂起的状态。

一个Express应用可以使用以下类型的中间件:

  • 应用级别的中间件
  • Router级别的中间件
  • 错误处理的中间件
  • 第三方中间件

你可以使用一个可选的安装路径来加载应用级别的中间件和Router级别的中间件。当然,你可以同时加载一系列中间件函数,在一个挂载点上创建一个中间件系统的子栈。

1.应用级别的中间件

使用app.use()app.METHOD()将应用级别的中间件绑定到app对象实例中。其中METHOD是它要处理的HTTP请求方法,例如GETPOST等(不过名字要小写)。

var app = express();

// 没有路径的中间件,向应用发送的任何请求都会被执行
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 中间件的路径 "/user/:id"; 对"/user/:id"以任何方法发送请求都会执行
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 一个路由和中间件(中间件系统),将会处理对"/user/:id"发送的GET请求
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

下面的例子,在同一路径的同一挂载点上加载了一系列中间件:

// 一个中间件子栈,将会打印出以任何方法类型对"/user/:id"请求的信息 
app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

路由处理器允许你为同一条路径定义多个路由。下面的例子为对/user/:id路径发送的请求定义了两个路由。第二个路由不会引起任何问题,但是它永远不会被执行,因为第一个路由结束了request-response循环。

// 一个中间件子栈处理对"/user/:id"发送的GET请求
app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// 处理"/user/:id",将打印出`user id` 
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

为了跳过来自router中间件栈中的中间件的剩下部分,调用next('route')方法将控制器交给下一个路由。注意:next('route')这个方法只对使用app.METHOD()router.METHOD()加载的中间件其作用。

// 中间件子栈来处理对"/user/:id"发送的GET请求
app.get('/user/:id', function (req, res, next) {
  // 如果用户id为0, 跳过剩下的部分,来到下一个路由
  if (req.params.id == 0) next('route');
  // 否则将控制器权交给这个栈中的下一个中间件
  else next(); //
}, function (req, res, next) {
  // 渲染"regular"页面
  res.render('regular');
});

// 处理对" /user/:id"的请求,渲染"special"页面
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

2.Router级别的中间件

Router级别的中间件与应用级别的中间件工作方式一样,只不过它被绑定到express.Router()实例上。

var router = express.Router();

使用router.use()router.METHOD()方法来加载Router级别的中间件。

以下的代码使用Router级别的中间件替代上面的应用级别的中间件系统。

var app = express();
var router = express.Router();

// 没有路径的中间件,对router发送的每条请求都会执行
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

//中间件子栈,处理以任何方法对"/user/:id"发送的请求,打印出请求信息 
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 中间件子栈处理对"/user/:id"发送的GET请求
router.get('/user/:id', function (req, res, next) {
  //如果用户id为0, 跳过剩下的部分,来到下一个路由
  if (req.params.id == 0) next('route');
  // 否则将控制器权交给这个栈中的下一个中间件
  else next(); //
}, function (req, res, next) {
  // 渲染"regular"页面
  res.render('regular');
});

// 处理对" /user/:id"的请求,渲染"special"页面
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// 在应用上安装router
app.use('/', router);

3.处理错误的中间件

处理错误的中间件总是有四个参数,你必须要提供四个参数来把它标识为处理错误的中间件。即使你不需要next对象,你也要在签名中保持它,否则的话,这个中间件会被当作是普通的中间件,而不能够处理错误。

定义处理错误的中间件和定义其它中间件一样,只不过它有四个参数,而不是三个,函数签名是(err, req, res, next)

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

更多关于处理错误的中间件的细节,请看错误处理

四.内置的中间件

在4.x版本中,Express不再依赖于Connect这个中间件,除了express.static之外,之前包含在Express中的所有中间件现在都在单独的模块中,请看中间件列表

express.static(root, [options])

Express唯一内置的中间件是express.static,它建立在serve-static基础上,负责对应用中的静态文件服务。

root参数声明了静态资源所在的根目录。

optional选项对象可以有以下的属性:

对以上出现的名称进行解释:

  • dotfiles:linux下(mac下)有各种app,每个人会根据自己的喜好和习惯来设置(快捷键,变量等等),而dotfiles就是保存了这些自定义设置的文件。
  • -

以下的例子中,使用了带有详细options对象的express.static中间件。

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

每个应用可以拥有不止一个静态文件目录:

app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

更多关于serve-static和它的选项,请看serve-static文档

五.第三方中间件

使用第三方中间件来为Express应用增加功能。

安装Node模块来满足相应的功能,在应用中以应用或者router级别来加载它。

以下的例子展示了安装并加载了用于cookie解析的中间件cookie-parser

安装:

$ npm install cookie-parser

加载:

var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// 加载`cookie-parser`中间件
app.use(cookieParser());

看一下第三方中间件,你会发现在Express中经常使用的部分第三方中间件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值