Node.js(四)——Express的应用
分类:nodejavascript
记得很久以前看了一本书,书名叫做《express开发Node》之类的,忘了具体是什么名字了,这里说这个的原因,就是说用express开发Node 的web确实很快捷,而且它和django基于python还不一样,django框架很大。而express的就比较轻量一点。 express可以让我们很轻易的开发基于MVC模式的网站应用: 所谓MVC (Model-View-Controller,模型视图控制器)是一种软件的设计模式,它最早是 由 20 世纪 70 年代的 Smalltalk 语言提出的,即把一个复杂的软件工程分解为三个层面:模 型、视图和控制器。 模型是对象及其数据结构的实现,通常包含数据库操作。 视图表示用户界面,在网站中通常就是 HTML 的组织结构。 控制器用于处理用户请求和数据流、复杂模型,将输出传递给视图。
很早之前就写过了关于express+mongoDB的文章,今天又来写一遍,希望有新的收获吧。 Express+ejs搭建小小网站
首先说一下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.js
var 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 setup
app.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 handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.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的用法; 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);
关于next
控制权转移 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);
});
当访问被匹配到的路径时,如 http://localhost:3000/user/carbo,会发现终端中打印了 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方法如果接受了参数,即代表发生了错误。 使用这种方法可以把错误检查分段化,降低代码耦合度。
视图助手
express提供视图助手?说实话我在另外一本书里面并没有看到讲视图助手。。。
视图助手有两类,分别是静态视图助手和动态视图助手。 这两者的差别在于: 静态视图 助手可以是任何类型的对象,包括接受任意参数的函数,但访问到的对象必须是与用户请求无 关的;静态视图助手可以通过 app.helpers() 函数注册,它接受一个对象,对象的每个属性名 称为视图助手的名称,属性值对应视图助手的值。 而动态视图助手只能是一个函数,这个函数不能接受参数,但可以访问 req 和 res 对象。动态视图助手则通过 app.dynamicHelpers() 注册,方法与静态视图助手相同,但每个属性的值必须为一个函数,该函数提供 req 和 res。
这个express3.0+也变了==
说这个全局全局视图在我们在后面使用 session 时会发现它是非常有用的。 暂时跳过吧,到时候看session怎么用。
ejs
这里省略了,因为ejs语法和jekyll的liquid语法冲突,无法正确显示。 可以查看原文Express