爱妃科技Express(Node 的MVC 框架) | | 爱妃科技
正在加载
请稍等

菜单

红楼飞雪 梦

15526773247

文章

Home nodejs Express(Node 的MVC 框架)
Home nodejs Express(Node 的MVC 框架)

Express(Node 的MVC 框架)

nodejs by

虽然Node 提供的核心API 很强大,但其中很多功能还是过于底层。正如许多基于Ruby 的网站会使用Rails 和Sinatra 开发,而不是使用自己编写的Ruby 代码一样,Node 社区构建了许多高级的功能库,大大地方便了开发。虽然这些模块从技术上讲并不是Node 本身,但它们对完成工作非常重要,而且其中许多库自身就是成熟的项目。接下来我们将将讨论Node 社区提供的几个最流行及最有用的模块。

Express

Express(Node 的MVC 框架)也许是使用最广泛的Node 模块了,它吸取了Ruby的Sinatra 框架的精髓,并提供了许多功能,使得用Node 构建网站变得非常简单。

(1)、一个简单的Express应用

Express 通过路由定义的页面处理器来工作。路由可以是一个简单的路径,也可以比较复杂。处理器可以简单地输出Hello, world,也可以复杂得像一个与数据库交互的完整页面渲染系统。需要先运行

npm install express

 

来安装Express,然后才能开始使用它。下例演示了如何用Express 创建一个简单的应用。

创建一个简单的Express 应用

var express = require('express');
var app = express.createServer();
app.get('/', function (req, res) {
  res.send('hello world');
});
app.listen(9001);

 

从创建服务器的角度来看, 这段代码与http 模块非常相似, 但有几点更加直观。首先,app.get() 为特定的路由(例子中为’/’)创建了响应函数。与一般的http 服务器不同的是,Express 不是为一般的请求提供监听器,而是针对特定
的HTTP 动作提供监听器。所以get() 只会响应GET 请求,put() 只处理PUT 请求,等等。配合上我们指定的路由,你马上可以拥有更为强大的功能。一个典型的Express 程序会指定一系列表达式,Express 会为每个进入的请求对所有的表达式按序进行路由匹配,然后执行第一个匹配的表达式对应的代码。

我们还可以利用next() 函数,让Express 在特定的情况下跳过表达式,这
会在文章后面进行讨论。

接 下来看看我们是如何响应请求的。我们依然使用同http 中一样的response 对象,但Express 增加了send() 方法。因此,我们不需要手动提供HTTP 头或者是调用end() 方法,send() 方法会处理好如何发送HTTP 头等操作,并会自动包含end() 调用。这里需要了解的是,Express 是把http 的基础功能封装起来,并通过提供众多的功能来丰富它,使得创建真正的应用非常便捷。你不需要在每次处理HTTP 请求的时候自己编写代码来处理路由逻辑,Express 都为你处理好了。

(2)、在Express中设置路由

路由是 Express 的一个核心概念,也是Express 非常有用的原因之一。正如上面介绍的那样,路由通过实现同名的方法(如get()、post())来支持一个HTTP动作。路由包含了一个简单的字符串或 者一个正则表达式,并且可以包含变量声明、通配符及可选关键字标记。让我们看几个例子,先从下面这个例子开始

 通过变量和可选标记选择路由

var express = require('express');
var app = express.createServer();
app.get('/:id?', function (req, res) {
  if (req.params.id) {
    res.send(req.params.id);
  } else {
    res.send('oh hai');
  }
});
app.listen(9001);

 

本例演示了如何在一个路由中包含一个可选的变量id。 Express 并不关心变量的取名,但之后能够在回调函数中使用它。在Express 路由中,我们使用冒号(:)来标记想要使用的变量,那么在URL 中传递的字符串就会被捕获并保存在该变量中。Express 中的所有路由最终都会转变成正则表达式来处理(稍后会做更多介绍),并进行分词1 操作以便于应用代码的使用。

分词(tokenize)指的是把一个文本字符串按块或按词切分成一个个标记的过程。

 

这个功能实际上是Express 的一个子模块router 完成的。你可以参考router 模块的源代码来了解
更多关于路由正则表达式的细节。

正 则表达式用来匹配路由中下一个已知的词。注意,这个变量其实是可选的。如果你运行此程序,然后访问http://localhost:9001,就只会得 到“oh hai”的响应,因为你没有在端口后面用/ 带上路由的可选变量部分。如果随意带上点什么内容(只要不包含另外一个/ 在里面),就将在响应内容中得到一样的内容,因为匹配了id 关键字的内容会被保存在req.params.id 中。

Express 路由总是将/ 视作一个标记,而同时又会把请求末尾的/ 当做可选项,所以我们提供的路由/:id? 会匹配上localhost、 localhost/、localhost/tom 和localhost/tom/,但不包括localhost/tom/tom。路由中也可以使用通配符,如下例所示,(*) 会匹配所有的内容,直到下一个标记出现(非贪心的正则匹配)。

在路由中使用通配符

app.get('/a*', function (req, res) {
  res.send('a');
  // 匹配 /afoo /a.bar /a/qux 等
});
app.get('/b*/c*d', function (req, res) {
  res.send('b');
  // 匹配 /b/cd /b/cfood /b//c/d/ 等
  // 不匹配 /b/c/d/foo
});
app.get('*', function (req, res) {
  res.send('*');
  // 匹配 /a /c /b/cd /b/c/d /b/c/d/foo
  // 不匹配 /afoo /bfoo/cbard
});

 

当使用通配符来构建路由时,两个通配符之间的标记必须匹配,除非它 是可选的。通配符通常用在包含(.) 的文件名中。还需要注意的是,与许多其他正则表达式语言不同,* 表示的不是零个以上字符,它表示的是一个以上字符。一个斜杠(/)在匹配通配符的时候可以认为是一个字符。另外需要注意的是,路由是按顺序执行的。当多个 路由同时匹配上提供的URL 时,只有第一个匹配的路由会执行相关的动作,也就是说,如何安排路由的顺序是很重要的。在前面的例子里,即便通配符能够匹配所有的URL,它也只能捕获前 面的路由未能匹配的URL。你还可以用正则表达式来定义路由。如果使用了这个方法,路由不会对正则匹配的内容做进一步处理。所以当你想从URL 中提取变量时,需要用正则的语法进行处理。

用正则表达式来定义路由

var express = require('express');
var app = express.createServer();
app.get(/\/(\d+)/, function (req, res) {
  res.send(req.params[0]);
});
app.listen(9001);

 

在这个例子里,正则表达式只会匹配以数字开头的URL(\d 表示匹配任意的数字,+允许匹配1 个或多个)。这表示/ 不会被匹配,而/12 则会被匹配。其实,正则匹配调用的是RegExp.match() 方法,它会在一个较长的字符串中寻找匹配的部分内容,所以/12abc 也会被匹配。如果你想确保正则表达式匹配完整的路由,就需要在表达式的末尾添加$ 标记,如/\/(\d+)$/。因为$ 会检查是否为行尾,所以这个表达式只有结束了才会被匹配。也许你想让Express 的默认行为忽略URL 结尾的/,那么只要在所有的字符串末尾用\/?$ 替换$ 就可以了。

注意看我们在上例中是如何访问正则表达式中提取的变量的。当你在路由 中使用正则表达式时,可以通过req.params 数组来访问提取的变量。当router 模块把你提供的路由处理成正则表达式时,这也同样有效,只不过你可能更希望通过变量名来访问罢了(如之前的例子所示)。你也可以在路由的正则匹配时指定变 量的命名,如下面的例子所示。

用正则表达式的同时指定变量类型

var express = require('express');
var app = express.createServer();
app.get('/:id(\\d+)', function (req, res) {
  res.send(req.params[0]);
});
app.listen(9001);

 

在这个例子中,通过设置路由只匹配数字(正则表达式\d+)来限定 参数id 只能是数字。提取的变量依然可以通过req.params.id 得到,但前提是能够被该正则匹配。因为正则表达式非常灵活,所以你可以用此技术来捕捉或限制URL 该匹配的情况,并能够方便地使用命名变量。注意,当你在JavaScript 字符串里输入反斜杠(\)时,需要对它进行转义。(但用正则表达式来定义路由那个例子中的情况不需要进行转义操作,因为那是直接在正则表达式中使用,而不 是在字符串中。)有时候,你会希望同一个URL 在不同的情景下匹配上多个路由。我们已经看到了路由定义的顺序会决定哪个路由被选中使用。但是,当某些条件不满足的时候(如下例),依然有办法可以把控制 权传给下一个路由,这在许多情况下会很有用。

把控制权传给下一个路由

app.get('/users/:id', function (req, res, next) {
  var id = req.params.id;
  if (checkPermission(id)) {
    // 显示个人页面
  } else {
    next();
  }
});
app.get('/users/:id', function (req, res) {
  // 显示公共页面
});

 

我们对路由的处理函数增加了一个新的参数,next 参数会通知路由中间件去调用下一个路由(稍后我们会详细介绍中间件)。这个参数总是会传递给回调函数的,只不过我们在这个例子里是第一次为它命名并使用 它。在这个例子里,我们通过检查id来查看用户是否有权限查看该页面的私有版本。如果没有权限,就把他带到下一个显示公共版本的路由去。这还能与 app.all() 方法很好地配合,它表示所有的HTTP 动作都进行处理。如下例演示的内容,我们可以同时捕捉各种HTTP 动作及路由,添加一些处理逻辑,然后把控制权交给更加具体的路由。

使用app.all() 来处理不同的HTTP 动作和路由,然后交回控制权

var express = require('express');
var app = express.createServer();
var users = [
  {
    name: 'tj'
  },
  {
    name: tom
  }
];
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 with ID: ' + 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('Danger, Will Robinson!', 404);
});
app.listen(3000);

 

这个例子与前一个相似,我们在传递控制权之前先检查该用户是否存 在。但是,我们不但对后续的路径进行此操作,同时还对所有的HTTP 操作都进行了检查。通常只会有一个路由被匹配上,因此这样的写法没有什么区别。但重要的一点是,要注意如何在路由间直接传递数据。因为中间件拥有 request 对象,所以当app.all() 方法中添加了req.user 属性后,后续所有的方法都能访问到它。每当回调函数被触发时,变量.req 其实只是指向中间件所持有的request 对象。所以使用中间件的所有函数和路由对request 对象的任何操作都是可见的。下面的例子展示了在特定范围内如何把一个文件扩展名设置为可选或者是必选。在第一个get() 方法里,:format 参数是可选的(正如? 所标记的),所以Express 会对
用户请求的ID 进行响应,而不会在乎其请求的是哪种格式。它会交给程序员来根据不同的格式(JSON、XML、文本等)采取相应的处理。

在第二个例子中,:format 参数期望匹配的类型是json 或xml。如果没有匹配的类型,即便:id 参数是有效的,请求book 的操作也不会进行。这让我们在决定处理哪些请求时有了很强的控制权,并且能够确保只有符合的格式才会被处理并生成响应内容。

可选或必填的路由扩张项

var express = require('express');
var app = express.createServer();
app.get('/users/:id.:format?', function (req, res) {
  res.send(req.params.id + '<br/>' + req.params.format);
  // 会响应:
  // /users/15
  // /users/15.xml
  // /users/15.json
});
app.get('/books/:id.:format((json|xml))', function (req, res) {
  res.send(req.params.id + '<br/>' + req.params.format);
  // 会响应:
  // /books/7.json
  // /books/7.xml
  // 但不会处理:
  // /books/7
  // /books/7.txt
});
app.listen(8080);

 

(3)、处理表单数据

大部分例子都演示了如何使 用GET 动作,其实Express 是以Ruby on Rails 的风格来提供RESTful 的架构的。利用Web 表单的隐藏项,你可以让表单进行各种操作:PUT(替换数据)、POST(创建数据)、DELETE(删除数据)和GET(获取数据)。我们来看一下下面 的例子。

用Express 处理表单

var express = require('express');
var app = express.createServer();
app.use(express.limit('1mb'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.get('/', function (req, res) {
  res.send('<form method="post" action="/">' +
  '<input type="hidden" name="_method" value="put" />' +
  'Your Name: <input type="text" name="username" />' +
  '<input type="submit" />' +
  '</form>');
});
app.put('/', function (req, res) {
  res.send('Welcome, ' + req.body.username);
});
app.listen(8080);

 

这个简单的例子演示了如何使用表单。首先, 创建一个Express 应用并配置使用bodyParser() 和methodOverride() 方法。bodyParser() 方法会解析从Web 浏览器发送来的请求正文, 并把表单变量转换成Express 使用的对象methodOverride() 方法允许表单提交隐藏的_method 变量,并把GET 方法替换掉,然后调用相应的RESTful 方法类型。express.limit() 方法指示Express 把请求正文的大小限制在1MB 以内。这是一个很重要的安全考虑,因为如果没有这个设置,外界就可以给应用发送一个超大的内容并让bodyParser() 来处理,这样就很容易进行一个拒绝服务(denial-ofservice,Dos)攻击。

请确保在bodyParser() 之后再调用methodOverride() 方法,否则,当
Express 检查是否要处理GET 方法或者其他命令时,表单变量就无法被
处理。

(4)、 模板引擎

显 然,直接在应用代码里继续手写HTML 代码并不明智。对初学者来说,它既不可读也不可维护,但更为重要的原因是,把代码逻辑与表现层标记混合在一起是一个糟糕的做法。模板引擎允许程序员把精力 集中在如何把信息呈现给用户上(通常以不同的格式,如显示器或手机格式),并把嵌入专用数据的过程从处理流程中分离开来。
Express 是最小化的集合,因此并没有内置模板引擎,而是由社区开发的模块来竞争。一些流行的引擎有Haml、Jade、Embedded Javascript(EJ)、CoffeeKup(基于CoffeeScript 的引擎)和jQuery 模板。下面的例子 中,应用的功能是渲染一个简单的Jade 模板。

Express 中使用简单的Jade 模板

var express = require('express');
var app = express.createServer();
app.get('/', function (req, res) {
  res.render('index.jade', {
    pageTitle: 'Jade Example',
    layout: false
  });
});
app.listen(8080);

 

运行此例子,需要先安装Jade 模板引擎:

npm install jade

 

首 先注意的是,代码里并不需要引用Jade 库,Express 会解析视图使用的模板文件名,并根据扩展名来确定该用哪个模板引擎(本例子中是index.jade 的jade)。因此,我们可以在一个项目中混合使用多种模板引擎。比如,项目并不会限制你只使用Jade 或只使用CoffeeKup,而是可以同时使用两个。这个例子传了两个参数给render 函数,第一个是用来显示视图的名字,第二个包含了配置项以及渲染需要的变量。我们稍后再回过头来讨论文件名参数。在本例里,我们传给视图两个变 量:pageTitle 和layout。layout 变量在这个例子中比较特别,因为它被设置为false,意思是让Jade 模板引擎在渲染index.jade 的时候,不要采用layout 模板文件(这一点后面会详细讲)。pageTitle 是一个本地变量,并会被视图内容所使用。它代表了模板的使用原理:在index.jade 文件中指定的HTML 内容中,有一个叫做pageTitle 的占位符,Jade会把我们传递的值替换上去。第一个参数index.jade 文件需要放在views 文件夹下(/views/index.jade),如下例所示。

Express 使用的简单Jade 文件

!!! 5
html(lang="en")
    head
        title =pageTitle
    body
        h1 Hello, World
        p This is an example of Jade.

 

在Jade 把我们提供的pageTitle 值填入页面后,渲染得到的效果如下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Jade Example</title>
    </head>
    <body>
        <h1>Hello, World</h1>
        <p>This is an example of Jade.</p>
    </body>
</html>

 

Jade 模板引擎通过把使用标记降低到最少,使得页面尽量简洁。你也许已经熟悉HTML 里的各种标签的结束记号了,Jade 取代它们的方法是根据缩近来确定页面的结构,所以得到的文件非常干净且容易阅读。第1 行!!! 5 表示内容类型为HTML5, 它会在输出结构中声明HTML5 的doctype。Jade 支持的默认文件类型有5、xml、default(XHTML 1.0 Transitional)、transitional(默认类型)、strict、frameset、1.1、basic 和mobile。同时,你也可以指定自己的格式,比如doctype html PUBLIC “-//W3C//DATA XHTMLCustom 1.10a//DE”。我们来看一下Jade 输入的第4 行的title 标记,Jade 会把字符串=pageTitle 解析为“把变量pageTitle 的内容插入到此处”。在输出结果中,内容变成了Jade
Example,这正是前文代码提供的值。我们已经介绍过,还有许多其他的模板选择,这些模板的功能也和Jade 相差无几,只不过各有各的语法和约束。

布局与子视图

布局能让你的网站实现视图中常用元素的共享,把内容与数据进一步分离开。通过把布局中的部件标准化,如导航、页眉和页脚,你可以把开发精力集中在网页视图的实际内容上。下面的例子 把刚才讨论的视图引擎例子放在一个“真实”的网站上了。

定义Express 中的全局模板引擎

var express = require('express');
var app = express.createServer();
app.set('view engine', 'jade');
app.get('/', function (req, res) {
  res.render('battlestar')
});

 

这个例子中新增加了set 命令的view engine 参数,现在Express 把Jade 引擎作为默认的模板引擎了,但依然允许在render 调用的时候修改掉。render 函数与之前的大不一样,因为已经设置了Jade 作为默认模板引擎,例子中无需指定文件全名,battlestar 实际代表了/views/battlestar.jade。如下例 所示,Express 会使用放在views/layout.jade 中的文件作为布局,因此不需要再像Express 中使用简单的Jade 模板那个例子一样样把layout 设置为false。

 Express 中的layout 文件

html
    body
        h1 Battlestar Galactica Fan Page
        != body

 

layout 文件与之前创建的视图文件很像,但是在这个例子中有一个特殊的body 变量。现在解释一下!= body 这行的意思,注意不要和文件顶部的body 关键词混淆。第二个body 并不是从应用代码传递过来的一个变量名,那么它是从哪里来的呢?当layout 选项在Express 中设置为true 时(默认),render 方法会把第一个参数的内容解析出来,然后把渲染好的输出以变量body 传递给layout,battlestart.jade文件如下例 所示。

Express 中的Jade 子视图

p Welcome to the fan page.

 

这里之所以称为子视图,是因为它并没有包含需要生成页面的所有内容,它需要结合layout 才能变成有用的输出结果。最终在Web 浏览器的输出如下:

<html>
    <body>
        <h1>Battlestar Galactica Fan Page</h1>
        <p>Welcome to the fan page.</p>
    </body>
</html>

 

子视图很有用,因为它使得开发者可以把注意力集中在需要显示的特定内容上,而不需要关注整个网页。这意味着内容不需要绑定在某个网页上,而且也可以作为手机页面的输出、AJAX 调用的结果(页面内刷新),等等。

注意不要混淆了变量body 与关键字body,变量body 包含了你的视图的
实际内容,而关键字body 是Web 浏览器使用的一个HTML 标签。

(5) 中间件

在 这之前的例子中,有些地方包含了看起来无关的函数app.use()。这个函数调用了Connect 库,并提供了许多有用的工具,使得添加功能很容易。现在是时候进一步介绍这个神奇的黏合剂(中间件),以及它为什么对开发Express 程序如此重要了。“中间件”(middleware)这个名称可能听起来有点像那些爱显摆的程序员喜欢使用的难懂术语,但正如前面提到的,中间件指的是链 接两个程序的一个软件,并且通常是更高级的程序间或者更宽广的网络间的软件。在实际工作中,中间件好比你家里或办公室里看得见的电话线,所有电话(应用) 都连接到电话线(中间件)
上,而后者能把应用与对应底层的网络链接起来。你的电话也许支持呼叫等待或语音信箱,但不管什么功能,线路工作方式都一样。你可以把语音信箱内置在电话中,或者是通过电信公司(网络)提供,但无论哪种情况,线路本身都乐于为你服务

Connect 库提供了Express 使用的中间件功能(见下表)。如图所示a,Connect 扩展了Node 的基础http 模块,为它赋予了http 能提供的所有基础服务,然后在此基础上又增加了自己的功能。Express 是从Connect 继承下来的,同时获得了http 和Connect 的功能。任何添加到Connect 的模块都会自动被Express 所使用。Connect是链接Express 和网络的中间层,它提供及使用了众多的功能,这些功能也许不能被Express 直接使用,但可用性都是一样的。最后,因为Express 本身从Connect 而来,所以Connect 的大部分功能都能直接从Express 中使用,这使得你可以使用app.
bodyParser() 这类命令,而不需要调用connect.bodyParser() 之类。

 


Connect 库提供的Express 使用的中间件功能

 名  称  描  述
 basicAuth  回调函数接受username 和password 参数,如果该证书允许访问网站,则函数
返回值为真
 bodyParser  解析请求正文的内容
 compiler  把.sass 和.less 文件编译 成CSS,把CoffeeScript 文件编译成JavaScript
 .cookieParser  解析从浏览器发送过来的请求包头中cookie 的内容
 csrf  通过改变额外的表单变量提供伪造跨域请求(CSRF), 依赖session 和
bodyParser 中间件
 directory  打印根路径下的文件夹,可以选择是否显示隐藏文件和图标
 errorHandler  捕获应用遇到的错误,提供选择把错误记录到stderr 或者是其他格式(JSON、纯
文本或HTML)
 favicon  通过缓存控制,从内存提供favicon 文件服务
 limit  限制服务器能接受的请求的大小,避免DoS 攻击
 logger  在响应时(默认)或接收请求时,把请求内容记录到标准输出或文件,并且允许
使用多种格式。通过改变缓存大小来控制写到磁盘的频率
 methodOverride  结合bodyParser 提供DELETE、PUT 以及POST 方法。允许更为明确的路由定
义,比如,使用app.put() 来替代在app.post() 中检查用户的意图。这个技
术能够帮助设计RESTful 应用。
 profiler  一般是放在其他所有中间件的前面,它会记录请求对应的响应时间和内存使用情况
 query  解析query 字符串,然后保存到req.query 参数中
 responseTime  生成响应内容时,把时间(单位是毫秒)填入X-Response-Time 头
 router  提供高级路由功能
 session  让多个请求共享用户数据的session 管理
 static  允许从根目录提供静态文件服务。支持部分下载和自定义过期时限
 staticCache  为static 中间件增加缓存层,通过把热门文件放在内存而提高响应速度
 vhost  允许在单一机器上以不同的vhost 提供站点服务

 

 

中间件工厂

到目前为止,你也许注意到了中间件包含的功能比起Express 顺序执行的函数要多一些。JavaScript 的闭包让我们能够在Node 内部实现工厂模式,并且能为你的网站路由提供上下文处理功能。

工厂是一个对象,它根据指定的参数创建出其他对象来。而如果采用手工创建对象的方式,会导致
出现大量重复或复杂的代码。

Express 的路由功能会在处理环节使用内部的中间件,可以通过重载来添加额外的功能,比如,在HTML 输出中添加自定义头。我们来看一下例,看看如何利用中间件工厂来拦截一个页面请求,并且强制进行角色授权验证。

Express 中的中间件工厂

var express = require('express');
var app = express.createServer(express.cookieParser(), express.session({
  secret: 'secret key'
}));
var roleFactory = function (role) {
  return function (req, res, next) {
    if (req.session.role && req.session.role.indexOf(role) != - 1) {
      next();
    } else {
      res.send('You are not authenticated.');
    }
  }
};
app.get('/', roleFactory('admin'), function (req, res) {
  res.send('Welcome to Express!');
});
app.get('/auth', function (req, res) {
  req.session.role = 'admin';
  res.send('You have been authenticated.');
});
app.listen(8080);

马上就可以测试一下, 访问http://localhost:8080/, 你会收到消息“You are notauthenticated.”。但是,如果看一下’/’ 路由对应的内容,你会发现实际上页面的内容是’Welcome to Express!’。第二个参数roleFactory(‘admin’) 会在页面显示前被调用,并检测到你的session 中没有role 属性,所以它停止了页面执行并且输出自己的消息。如果访问http://localhost:8080/auth 之后, 接着访问http://localhost:8080/, 你会得到消息“Welcome to Express!”。在此情景下,/auth URL 往你的session 的role 属性上添加了’admin’ 变量,所以当roleFactory 执行时,它会把执行控制交给
next(),也就是app.get(‘/’) 函数。因此,我们可以说,通过内部的中间件,我们把执行顺序改为:

  • (1) roleFactory(‘admin’)
  • (2) app.get(‘/’)

如果想在检查授权时多增加一些角色呢?可以把路由改为:

var powerUsers = [
  roleFactory('admin'),
  roleFactory('client')
];
app.get('/', powerUsers, function (req, res) {
  res.send('Welcome to Express!');
});

因为传入了一组中间件,所以就把页面执行限制成了同时是admin 和client 角色的用户,并把执行顺序改为了:

  • (1) roleFactory(‘admin’)
  • (2) roleFactory(‘client’)
  • (3) app.get(‘/’)

因为每个roleFactory 方法都要求对应的角色在session 中出现,所以用户必须同时是client 和admin 才能访问该页面。

 

01 2015-07

 

我要 分享

 

 

本文 作者

 

相关 文章