本文共 7918 字,大约阅读时间需要 26 分钟。
记得很久以前看了一本书,书名叫做《express开发Node》之类的,忘了具体是什么名字了,这里说这个的原因,就是说用express开发Node 的web确实很快捷,而且它和django基于python还不一样,django框架很大。而express的就比较轻量一点。
express可以让我们很轻易的开发基于MVC模式的网站应用: 所谓MVC (Model-View-Controller,模型视图控制器)是一种软件的设计模式,它最早是 由 20 世纪 70 年代的 Smalltalk 语言提出的,即把一个复杂的软件工程分解为三个层面:模 型、视图和控制器。 模型是对象及其数据结构的实现,通常包含数据库操作。 视图表示用户界面,在网站中通常就是 HTML 的组织结构。 控制器用于处理用户请求和数据流、复杂模型,将输出传递给视图。很早之前就写过了关于express+mongoDB的文章,今天又来写一遍,希望有新的收获吧。
首先说一下express的命令,可以自动生成一个基于express的nodejs应用,这一点之前不会。确实很强大。
当然,首先你需要装express,这里就不说了。 装好了之后可以通过express -h,查看一下express有哪些功能。 这里有两个命令很关键 -v -c -v是指定以什么模版生成 -c是指定以什么css语法生成,默认的模版是jade和css,这里我使用ejs和sass,指定项目名称为myApp。 因此命令如下: express –view=ejs –css=sass myApp 生成后的目录如下: 不过这个项目还不能马上用,需要首先 npm install –save-dev,完毕之后就可以使用了。 现在我们来看一下每一个文件吧。 app.jsvar express = require('express');var path = require('path');var favicon = require('serve-favicon');var logger = require('morgan');var cookieParser = require('cookie-parser');var bodyParser = require('body-parser');var index = require('./routes/index');var users = require('./routes/users');var app = express();// view engine setupapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');// uncomment after placing your favicon in /public//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));app.use(logger('dev'));app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false }));app.use(cookieParser());app.use(require('node-sass-middleware')({ src: path.join(__dirname, 'public'), dest: path.join(__dirname, 'public'), indentedSyntax: true, sourceMap: true}));app.use(express.static(path.join(__dirname, 'public')));app.use('/', index);app.use('/users', users);// catch 404 and forward to error handlerapp.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err);});// error handlerapp.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error');});module.exports = app;app.listen(3000);
首先我们导入了 Express 模块,前面已经通过 npm 安装到了本地,在这里可以直接通过 require 获取。
routes 是一个文件夹形式的本地模块,即./routes/index.js,它的功能 是为指定路径组织返回内容,相当于 MVC 架构中的控制器。 通过 express() 函数创建了一个应用的实例,后面的所有操作都是针对于这个实例进行的。 接下来是三个 app.configure 函数,分别指定了通用、开发和产品环境下的参数。 第一个 app.configure 直接接受了一个回调函数,后两个则只能在开发和产品环境中调用。app.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');
app.set第一个设置views的路径,第二个指定使用什么模版渲染。
app.set 是 Express 的参数设置工具,接受一个键(key)和一个值(value),可用的参 数如下所示。
basepath:基础地址,通常用于 res.redirect() 跳转。 views:视图文件的目录,存放模板文件。 view engine:视图模板引擎。 view options:全局视图参数对象。 view cache:启用视图缓存。 case sensitive routes:路径区分大小写。 strict routing:严格路径,启用后不会忽略路径末尾的“ / ”。 jsonp callback:开启透明的 JSONP 支持。app.use(logger('dev'));app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false }));app.use(cookieParser());app.use(require('node-sass-middleware')({ src: path.join(__dirname, 'public'), dest: path.join(__dirname, 'public'), indentedSyntax: true, sourceMap: true}));app.use(express.static(path.join(__dirname, 'public')));app.use('/', index);app.use('/users', users);
这里用了了5个中间件:bodyParser、cookieParse、router、static 以及 路由控制。 bodyParser 的功能是解析客户端请求,通常是通过 POST 发送的内容。methodOverride 用于支持定制的 HTTP 方法1。
router 是项目的路由支持。 static 提供了静态文件支持。 errorHandler 是错误控制器。 看最新的API文档,看到express.static(root, [options])的解释: This is the only built-in middleware function in Express. It serves static files and is based on serve-static. 说static是Express中惟一的内置中间间方法? 提供了静态服务的存放地址,这个没错,我们的css文件以及js、img文件全部都统统放在这里,放在其他地方就不行了。不过唯一的中间件,我就不知道怎么解释了。再细说一下app.use的用法;
app.use([path,] callback [, callback…]) 其实看API文件就能很清楚,第一个参数是路径(可选),第二个参数是回调函数,必须有一个以上的回调函数。 关于路径:The path for which the middleware function is invoked; can be any of:
A path pattern. A regular expres A regular expression pattern to match paths. An array of combinations of any of the above. 也就是说这个路径可以是一个路径名比如:’/users’,也可以是一个路径正则匹配’/*’,也可以缺省。 指定了路径之后,满足该路径的请求就会执行后面的回调函数。 缺省的话就是所有请求都执行这个回调函数。
关于回调函数:
有以下几种情况A middleware function.
A series of middleware functions (separated by commas). An array of middleware functions. A combination of all of the above.
总结就是,可以是中间件函数,可以是一系列中间间函数用comma分割,可以是一个中间间函数的数组,或者是以上所有的情况的综合。
例如:
app.use(function (req, res, next) { next();});
一个路由也可以当作一个 中间件函数
var router = express.Router();router.get('/', function (req, res, next) { next();});//一个路由app.use(router);
var r1 = express.Router();r1.get('/', function (req, res, next) { next();});var r2 = express.Router();r2.get('/', function (req, res, next) { next();});app.use(r1, r2);
var r1 = express.Router();r1.get('/', function (req, res, next) { next();});var r2 = express.Router();r2.get('/', function (req, res, next) { next();});app.use('/', [r1, r2]);
function mw1(req, res, next) { next(); }function mw2(req, res, next) { next(); }var r1 = express.Router();r1.get('/', function (req, res, next) { next(); });var r2 = express.Router();r2.get('/', function (req, res, next) { next(); });var subApp = express();subApp.get('/', function (req, res, next) { next(); });app.use(mw1, [mw2, r1, r2], subApp);
控制权转移
Express 支持同一路径绑定多个路由响应函数,例如:app.all('/user/:username', function(req, res) { res.send('all methods captured');});app.get('/user/:username', function(req, res) { res.send('user: ' + req.params.username); });
但当你访问任何被这两条同样的规则匹配到的路径时,会发现请求总是被前一条路由规 则捕获,后面的规则会被忽略。原因是 Express 在处理路由规则时,会优先匹配先定义的路 由规则,因此后面相同的规则被屏蔽。
Express 提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用 next(),会将路由控制权转移给后面的规则,例如:app.all('/user/:username', function(req, res, next) { console.log('all methods captured');next();});app.get('/user/:username', function(req, res) { res.send('user: ' + req.params.username); });
当访问被匹配到的路径时,如 ,会发现终端中打印了 all methods captured,而且浏览器中显示了 user: carbo。这说明请求先被第一条路由规 则捕获,完成 console.log 使用 next() 转移控制权,又被第二条规则捕获,向浏览器 返回了信息。
这是一个非常有用的工具,可以让我们轻易地实现中间件,而且还能提高代码的复用程 度。例如我们针对一个用户查询信息和修改信息的操作,分别对应了 GET 和 PUT 操作,而 两者共有的一个步骤是检查用户名是否合法,因此可以通过 next() 方法实现:var users = { 'byvoid': { name: 'Carbo', website: 'http://www.byvoid.com' }};app.all('/user/:username', function(req, res, next) { // 检查用户是否存在if (users[req.params.username]) {next(); } else {next(new Error(req.params.username + ' does not exist.')); }});app.get('/user/:username', function(req, res) { // 用户一定存在,直接展示res.send(JSON.stringify(users[req.params.username])); });app.put('/user/:username', function(req, res) { // 修改用户信息res.send('Done');});
上面例子中,app.all 定义的这个路由规则实际上起到了中间件的作用,把相似请求 的相同部分提取出来,有利于代码维护其他next方法如果接受了参数,即代表发生了错误。 使用这种方法可以把错误检查分段化,降低代码耦合度。
说一下ejs:
<% code %>:JavaScript 代码。 <%= code %>:显示替换过 HTML 特殊字符的内容。 例如 模版文件是:{ title: 'Cleaning Supplies'supplies: ['mop', 'broom', 'duster'] }
1、添加title:
<%= title %>
2、添加链接
<%= link_to(supplies[i], 'supplies/'+supplies[i]) %>
3、添加图片
<%= img_tag('maid.jpg') %>
具体可以看ejs的官网
回到NodeJs开发指南这本书片段视图:
app.jsapp.get('/list', function(req, res) { res.render('list', { title: 'List',items: [1991, 'byvoid', 'express', 'Node.js'] });});
在 views 目录下新建 list.ejs,内容是:
同时新建 listitem.ejs,内容是:
访问 ,可以在源代码中看到以下内容
… 这个时候提示 partial is not defined 最后去查了原因: 是因为partial 函数已经摒弃,使用foreach,include代替ok,正确显示了。
express提供视图助手?说实话我在另外一本书里面并没有看到讲视图助手。。。
视图助手有两类,分别是静态视图助手和动态视图助手。
这两者的差别在于: 静态视图 助手可以是任何类型的对象,包括接受任意参数的函数,但访问到的对象必须是与用户请求无 关的;静态视图助手可以通过 app.helpers() 函数注册,它接受一个对象,对象的每个属性名 称为视图助手的名称,属性值对应视图助手的值。 而动态视图助手只能是一个函数,这个函数不能接受参数,但可以访问 req 和 res 对象。动态视图助手则通过 app.dynamicHelpers() 注册,方法与静态视图助手相同,但每个属性的值必须为一个函数,该函数提供 req 和 res。这个express3.0+也变了==
说这个全局全局视图在我们在后面使用 session 时会发现它是非常有用的。
暂时跳过吧,到时候看session怎么用。