三.使用模版引擎
在Express能够渲染模版文件之前,必须要对应用进行下面的设置:
views
:模版文件所在的目录,例如:app.set('views', './views')
view engine
:指定使用的模版引擎,例如:app.set('view engine', 'jade')
然后安装对应的模版引擎包:
$ npm install jade --save
能够迎合Express模版引擎(例如Jade),都会导出一个函数__express(filePath, options, callback)
,这个函数会被res.render()
调用来渲染模版代码。
一些模版引擎不遵守这个约定, Consolidate.js 这个库可以让所有的nodejs模版引擎遵守这个约定,这样就能让这些模版引擎在Express中工作。
一旦设置了view engine
(视图引擎),你就不必在你的应用中显式地声明模版或者加载模版引擎模块。Express会在应用中自动加载:
app.set('view engine', 'jade');
在views
(视图目录)中创建一个Jade
模版文件,命名为index.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中模版引擎如何工作的,请阅读为Express开发模版引擎
四.错误处理
定义处理错误的中间件和定义其它中间件一样,只不过它有四个参数,而不是三个,函数签名是(err, req, res, next)。
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
你要在最后定义错误处理的中间件,要放在app.use()
和路由调用之后,例如:
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
// logic
});
来自错误处理的中间件的响应是完全随意的,你可以返回一个HTML处理页面,一个简单的信息,一个JSON字符串,或者其它你想要设置的。
处于组织化的目的,你可能会定义几个错误处理的中间件。例如,你想定义一个错误处理器,负责处理通过XHR
(XMLHttpRequest)发送的请求。
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
方法logErrors
可能会将请求和错误消息写到stderr
,或其他相似的服务中。
function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}
clientErrorHandler方法像下面这样定义,注意错误被显式地传到next
中。
function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something blew up!' });
} else {
next(err);
}
}
下面的errorHandler方法定义:
function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}
如果你传递一些参数(除了字符串'route'
)给next()
函数的话,Express会把当前的请求当作有错误发生,会跳过任何剩下来的路由/中间件函数。如果你想要以某种方式处理所发生的错误的话,你必须要创建一个处理错误的路由。
如果你有一个包含多个回调函数的路由处理器的话,你可以使用route
这个参数来跳过剩下的部分直接来到下一个路由处理器。
app.get('/a_route_behind_paywall',
function checkIfPaidSubscriber(req, res, next) {
if(!req.user.hasPaid) {
// continue handling this request
next('route');
}
}, function getPaidContent(req, res, next) {
PaidContent.find(function(err, doc) {
if(err) return next(err);
res.json(doc);
});
});
在这个例子中,getPaidContent
这个处理器会被跳过,但是应用中剩下的对于路径/a_route_behind_paywall
的处理器都会被执行。
调用next()
和next(err)
表明当前的处理器是完整的,next(err)
会跳过所有剩下的处理器,除了那些被用来处理错误的处理器。
默认的错误处理器:
Express自身携带一个内置的错误处理器,它将负责处理发生在应用中的任何错误,这个默认的错误处理的中间件被放在中间件栈底。
如果你传递一个错误给next()
,你没有在一个错误处理器中处理这个错误,这个错误将会由Express内置的错误处理器负责处理。这个错误会连同堆栈轨迹
一同写进客户端。堆栈轨迹不会包含在生产环境中。
讲解:设置环境变量NODE_ENV
的值为production
,来让应用运行于生产模式中。
如果在你已经写了响应之后,调用包含一个错误的next()
函数,Express默认的错误处理器将会关掉连接,让这个请求被认为是失败的。
所以当你增加一个自定义的错误处理器的时候,你将想要委托给Express中的默认错误处理机制,尽管响应的头部已经发送给了客户端。
function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}
五.调试Express
Express使用debug这个模块来一步步地打印出关于路由匹配,使用的中间件,应用模式和request-response
循环流的相关信息。
debug
就像是console.log
的一个加强版本,但它不像console.log
,你不必再生产模式下注释掉debug
的打印日志。生产环境下它会被默认关闭掉,如果你可以设置环境变量DEBUG
来自由地控制。
为了看到Express内部使用的所有打印记录,当你启动应用的时候,设置环境变量DEBUG
的值为:express:*
,
$ DEBUG=express:* node index.js
在Windows平台上,使用对应的命令:
> set DEBUG=express:* & node index.js
在express generator
创建的默认应用中运行下面命令,将会打印出:
$ DEBUG=express:* node ./bin/www
express:router:route new / +0ms
express:router:layer new / +1ms
express:router:route get / +1ms
express:router:layer new / +0ms
express:router:route new / +1ms
express:router:layer new / +0ms
express:router:route get / +0ms
express:router:layer new / +0ms
express:application compile etag weak +1ms
express:application compile query parser extended +0ms
express:application compile trust proxy false +0ms
express:application booting in development mode +1ms
express:router use / query +0ms
express:router:layer new / +0ms
express:router use / expressInit +0ms
express:router:layer new / +0ms
express:router use / favicon +1ms
express:router:layer new / +0ms
express:router use / logger +0ms
express:router:layer new / +0ms
express:router use / jsonParser +0ms
express:router:layer new / +1ms
express:router use / urlencodedParser +0ms
express:router:layer new / +0ms
express:router use / cookieParser +0ms
express:router:layer new / +0ms
express:router use / stylus +90ms
express:router:layer new / +0ms
express:router use / serveStatic +0ms
express:router:layer new / +0ms
express:router use / router +0ms
express:router:layer new / +1ms
express:router use /users router +0ms
express:router:layer new /users +0ms
express:router use / <anonymous> +0ms
express:router:layer new / +0ms
express:router use / <anonymous> +0ms
express:router:layer new / +0ms
express:router use / <anonymous> +0ms
express:router:layer new / +0ms
当你对这个应用发送请求的时候,你将会看到日志:
express:router dispatching GET / +4h
express:router query : / +2ms
express:router expressInit : / +0ms
express:router favicon : / +0ms
express:router logger : / +1ms
express:router jsonParser : / +0ms
express:router urlencodedParser : / +1ms
express:router cookieParser : / +0ms
express:router stylus : / +0ms
express:router serveStatic : / +2ms
express:router router : / +2ms
express:router dispatching GET / +1ms
express:view lookup "index.jade" +338ms
express:view stat "/projects/example/views/index.jade" +0ms
express:view render "/projects/example/views/index.jade" +1ms
为了只查看由router产生的日志,设置DEBUG
变量的值为express:router
;同样地,如果只想查看由应用产生的日志,设置DEBUG
变量的值为express:application
express产生的应用:
由express
命令产生的应用也会使用DEBUG
这个模块,它的调试命名空间局限于应用的名字。
如果你使用下面的命令创建一个应用:
$ express sample-app
你可以使用以下的命令来激活调试状态:
$ DEBUG=sample-app node ./bin/www
你可以使用逗号将一系列名字分开来使用多个调试命令空间,如下:
$ DEBUG=http,mail,express:* node index.js
想要了解更多关于调试的信息,请阅读调试指南