本文档基于express4.x版本css
mkdir hello && cd hello && npm install express
新建app.js
内容以下node
var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('hello world'); }); app.listen(3000);
node app.js
,而后浏览器访问http://localhost:8888
能够看到hello world可使用express工具生成应用程序框架,jquery
npm install express-generator -g
express -h
显示选项express myapp
在当前目录建立名为myapp
的express程序cd myapp && npm install
安装所需模块set DEBUG=myapp & node ./bin/www
运行apphttp://localhost:3000
路由指肯定程序如何针对特定请求进行反应,这个请求是一个URI和它的HTTP请求方法。git
每个路由有一个处理函数,当路由匹配的时候执行。github
路由定义格式为:app.VERB(PATH, HANDLER)
,其中app
是一个express实例,VERB
是一个HTTP请求方法,PATH
是服务器上的一个路径,HANDLER
是用于处理请求的函数。web
一下代码展现app的基本路由正则表达式
// respond with "Hello World" on the homepage app.get('/', function (req, res) { res.send('Hello World'); }); // accept POST request on the homepage app.post('/', function (req, res) { res.send('Got a POST request'); }); // accept PUT request at /user app.put('/user', function (req, res) { res.send('got a PUT request at /user'); }); // accept DELETE request at /user app.delete('/user', function (req, res) { res.send('got a DELETE request at /user'); });
express程序实质上是一系列的中间件调用。数据库
中间件是一个能够访问请求、相应对象和下一个中间件的函数,中间件功能有:express
若是当前中间件不结束请求-响应周期,它必须调用next()
将控制传递给下一个中间件,不然请求将会悬挂。
中间件有一个可选的mount路径,中间件能够再应用程序级别或者路由级别进行加载。一样,一系列的中间件函数能够同时加载,这就在mount点建立了一个中间件子栈。
express应用程序可使用一下类型的中间件:
应用程序级别中间件绑定在express示例上,使用app.use()
或者app.VERB()
var app = express(); // a middleware with no mount path; // gets executed for every request to the app app.use(function (req, res, next) { console.log('Time: ', Date.now()); next(); }); // a middleware mounted on /user/:id; // will be executed for any type of HTTP request to /user/:id app.use('/user/:id', function (req, res, next) { console.log('Request Type: ', req.method); next(); }); // a route and its handler function (middleware system) // which handles GET requests to /user/:id app.get('/user/:id', function (req, res, next) { res.send('user'); });
下面代码在mount点加载一系列中间件
// a middleware sub-stack which prints request info // for any type of HTTP request to /user/:id app.use('/user/:id', function (req, res, next) { console.log('request url: ', req.originUrl); next(); }, function (req, res, next) { console.log('request type: ', req.method); next(); });
路由处理程序,做为中间件系统的一部分,使得为同一个路径定义多个路由成为可能。下面的例子中,为GET /user/:id
定义了两个路由。第二个路由没有任何语法问题,可是永远得不到调用,由于第一个路由终止了请求-响应循环。
// a middleware sub-stack which handles GET requests to /user/:id app.get('/user/:id', function (req, res, next) { console.log('id: ', req.params.id); next(); }, function (req, res, next) { res.send('user info'); }); // handler for /user/:id which prints the user id app.get('/user/:id', function (req, res, next) { res.end(req.params.id); });
若是想要跳过路由中间件栈剩下的中间件,调用next('route')
会将控制权转交给下一个路由。须要注意的是:next('route')
值能在使用app.VERB()
或者router.VERB()
加载的中间件中使用。
// a middleware sub-stack which handles // GET requests to /user/:id app.get('/user/:id', function (req, res, next) { // if user id is 0, skip to the next route if (req.params.id === 0) { next('route'); } // else pass the control to the next middleware in this stack else { next(); } }, function (req, res, next) { res.render('regular'); }); // handler for /user/:id which renders a special page app.get('/user/:id', function (req, res, next) { res.render('special'); });
路由级别中间件与应用程序级别中间件工做同样,惟一的区别是他们bound toexpress.Router()
的实例
var router = express.Router();
路由级别中间件经过router.use()
或者router.VERB()
加载。
前面应用程序级别的中间件可使用路由级别代替:
var app = express(); var router = express.Router(); // a middleware with no mount path, // get executed for every request to the router router.use(function (req, res, next) { console.log('Time:', Date.now()); next(); }); // a middleware sub-stack shows request // info for any type of HTTP request to /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.methos); next(); }); // a middle ware sub-stack which handles GET requests to /user/:id router.get('/user/:id', function (req, res, next) { // if user id is 0, skip to the next router if (req.params.id === 0) { next('route'); } // else pass the control to the next middleware in this stack else { next(); } }, function (req, res, next) { res.render('regular'); }); // handler for /user/:id which renders a special page router.get('/user/:id', function (req, res, next) { console.log(req.params.id); res.render('special'); }); // mount the router on the app app.use('/', router);
express4.X以后再也不依赖于Connect。除了自带express.static
以外,全部其余中间件都放到单独模块中。查看中间件列表
express.static
基于serve-static模块,负责为express应用程序提供静态资源服务。
root
参数为静态资源存放的根目录。
可选的options
对象能够有如下属性:
dotfiles
控制点文件服务,可选值为allow
,deny
,'ignore'默认为ignore
etag
控制etag生成,默认为true
extensions
设置文件后缀名补充,默认为false
index
设置目录访问的返回,默认为index.html
,设置为false
能够禁止目录访问lastModified
根据文件修改时间设置Last-Modified
报头,默认true
maxAge
设置Cache-Control报头的缓存控制时间,单位为毫秒,默认为0redirect
当路径名是目录时,重定向到包含结尾/
的目录,默认true
setHeaders
函数用于为文件设置HTTP头如下是例子:
var options = { dotfiles: 'ignore', etag: false, extensions: ['htm', 'html'], index: false, maxAge: '1d', redirect: false, setHeaders: function (req, res, 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查看详细选项
express是一个路由和中间件web框架,自身包含不多功能,能够经过第三方中间件添加所需功能。
安装所需模块而且在应用程序级别或者路由级别加载。
下面的例子使用cookie-parser
来解析cookie
npm install cookie-parser
var express = require('express'); var app = express(); var cookieParser = require('cookie-parser'); app.use(cookieParser());
异常处理中间件与其余中间件相比的区别是函数参数为四个(err, req, res, next)
app.use(function (err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); });
尽管不是强制的,一般来讲错误处理中间件定义在最后:
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请求定义错误处理程序。
var bodyParser = require('body-parser'); var methodOverride = require('method-override'); app.use(bodyParser()); app.use(methodOverride()); app.use(logErros); app.use(clientErrorHandler); app.use(errorHandler);
其中logErrors将请求和错误信息输出到stderr
function logErrors(err, req, res, next) { console.error(err.stack); next(err); }
clientErrorHandler定义以下:
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 }); }
express使用debug模块来log路由匹配,中间件使用,应用粗模式等。
debug
相似于console.log
可是不一样的是发布版本时不须要注释debug日志语句,经过环境变量DEBUG
调节日志输出
查看express内部日志的方法:启动app时设置DEBUG
环境变量为express:*
DEBUG=express:* node index.js
若是只想输出路由信息,设置DEBUG=express:router
,若是只想查看应用程序相关log,设置DEBUG=express:application
更多debug信息,查看debug guide
在express渲染模板文件以前,必须执行如下设置
app.set('views', './views')
app.set('view engine', 'jade')
而后安装对应模板引擎的package
npm install jade --save
兼容于express的模板引擎会暴露一个__express(filePath, options, callback)
的接口,用于提供给res.render()
调用来渲染模板
一些模板引擎不遵照这个约定consolidate.js能够为流行的node模板引擎建立转换,这样就能够与express无缝衔接。
模板引擎设置好以后,没必要显式指定模板引擎或者加载模块,express会自动加载
app.engine('jade', require('jade').__express);
在视图目录下建立一个jade模板文件index.jade
html head title!= title body h1!= message
而后建立一个路由渲染index.jade
app.get('/', function (req, res) { res.render('index', { title: 'hey', message: 'Hello there!' }); });
此时访问主页便可看到欢迎界面,查看如何开发express模板引擎能够学习更多知识
建立express应用程序
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('hello world'); }); app.listen(3000);
express应用程序选项能够经过app.set()
进行设置,经过app.get()
读取如下是支持的选项
trust proxy
说明app位于front-facing proxy以后,X-Forwarded-*
报头能够用来肯定客户端链接和IP地址。须要注意的是X-Forwarded-*
报头很容易造假,因此检测到的IP地址并不可靠。
trust proxy默认是禁止的。当激活时,express尝试获取经过front-facing proxy链接的客户端IP地址。req.ips
属性包含客户端链接中的IP地址。一下只能够用于设置:
Type | Value |
---|---|
Boolean | 若是是true,express假设客户端IP地址是X-Forwarded-*中最左的值。若是false,express认为直接链接客户端,req.connection.remoteAddress是客户端地址,默认值为false。 |
IP 地址 | 可信任的ip地址,子网或者IP地址数组。如下是预约义子网名字
IP地址能够以下设置 app.set('trust proxy', 'loopback'); // specify a single subnet app.set('trust proxy', 'loopback, 123.123.123.123'); // subnet and an address app.set('trust proxy', 'loopback, linklocal, uniquelocal'); app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client's IP address. |
Number | Trust the nth hop from the front-facing proxy server as the client. |
Function | 自定义信任实现。只有你知道本身在作什么的时候才这样作 app.set('trust proxy', function (ip) { if (ip === '127.0.0.1' || ip === '123.123.123.123') { return true; // trusted IPs } else { return false; } }); |
查看proxy-addr更多信息。
env
环境模式,默认为process.env.NODE_ENV
(NODE_ENV环境变量)或者development
subdomain offset
The number of dot-separated parts of the host to remove to access subdomain, two by default.jsonp callback name
修改json回调名字?callback=
json replacer
JSON replacer callback, 默认为nulljson spaces
设置为JSON缩进,默认禁止case sensitive routing
enable区分大小写,默认不区分,即/Foo
与/foo
同样strict routing
enable严格路由,默认状况下/foo
和/foo/
认为相同view cache
设置模板编译缓存,发布状况下默认激活view engine
引擎默认后缀名views
视图目录路径,默认为process.cwd() + '/views'
query parser
query解析器,simple
或者extended
,默认为extended
,简单query解析器基于node的原生解析器querystring
,扩展解析器基于qs x-powered-by
激活X-Powered-By: Express
HTTP报头,默认激活etag
设置ETag响应头
Type | Value |
---|---|
Boolean | true容许强ETag。这是默认设置。false禁止ETag |
String | 若是是`strong`使用强ETag。若是是`weak`使用弱ETag |
Function | 自定义ETag实现。只有你知道在作什么才使用它 app.set('etag', function (body, encoding) { return generateHash(body, encoding); }); |
为程序name设置value
app.set('title', 'My site'); app.get('title'); // My site
获取设置的name值
app.get('title'); // undefined app.set('title', 'My site'); app.get('title'); // My site
设置name属性为true
app.enable('trust proxy'); app.get('trust proxy'); // true
检查name是否激活
app.enabled('trust proxy'); // false app.enable('trust proxy'); app.enabled('trust proxy'); // true
设置name值为false
app.disable('trust proxy'); app.get('trust proxy'); // false
检查name是否禁止
app.disabled('trust proxy'); // true app.enable('trust proxy'); app.disabled('trust proxy'); // false
在path路径Mount中间件函数。若是path没有设置,默认为/
.
中间件mount到path以后当请求路径的起始部分匹配了path就会执行中间件。
因为path默认值为/
因此默认状况下全部的请求都会执行中间件
// this middleware will be executed for every request to the app app.use(function (req, res, next) { console.log('Time: %d', Date.now()); next(); });
匹配的中间件函数会顺序执行,因此中间件配置顺序很重要
// this middleware will not allow the request to go beyond it app.use(function (req, res, nex) { res.send('hello world'); }); // request will never reach this route app.get('/', function (req, res) { res.send('Welcome'); });
path
可使一个字符串,一个模式,一种正则表达式或者包含三种模式描述路径的数组。
Type | Example |
---|---|
Path | // will match paths starting with /abcd app.use('/abcd', function (req, res, next) { next(); }); |
Path Pattern | // will match paths starting with /abcd and /abd app.use('/abc?d', function (req, res, next) { next(); }); // will match paths starting with /abcd, /abbcd, /abbbbcd and so on app.use('/ab+cd', function (req, res, next) { next(); }); // will match paths starting with /abcd, abxcd, /abfoocd and so on app.use('/ab*cd', function (req, res, next) { next(); }); // will match paths starting with /ad and abcd app.use('/a(bc)?d', function (req, res, next) { next(); }); |
Regular Expression | // will match paths starting with /abc and /xyz app.use(/\/abc|\/xyz/, function (req, res, next) { next(); }); |
Array | // will match paths starting with /abcd, /xyza, /lmn, and /pqr app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], function (req, res, next) { next(); }); |
function
可使一个中间件函数,或者一系列中间件函数,或者包含中间件函数的数组或者他们的组合。因为路由和程序都实现了中间件接口,能够再其余地方像使用中间件那样使用。
Usage | Example |
---|---|
Single Middleware | 中间件函数能够定义,而且在本地mount app.use(function (req, res, next) { next(); }); 路由也是合法的中间件 var router = express.Router(); router.get('/', function (req, res, next) { next(); }); app.use(router); express应用程序也是合法中间件 var subApp = express(); subApp.get('/', function (req, res, next) { next(); }); app.use(subApp); |
Series of Middleware | 能够再一个mount路径使用多个中间件 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); |
Array | 将多个中间件组装到数组中,若是数组形式的中间件是第一个参数或者惟一参数,须要制定mount路径 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]); |
combination | 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.static中间件的例子。
从public
目录提供静态文件服务
// GET /styles.css etc app.use(express.static(__dirname + '/public'));
将中间件mount到/static
路径,这样只有请求路径以/static
开头才响应。
// GET /static/style.css etc. app.use('/static', express.static(__dirname + '/public'));
禁止静态文静请求log:将log中间件配置到静态文件中间件以后
app.use(express.static(__dirname + '/public')); app.use(logger());
在多个不一样目录提供静态文件服务,放在最前面的具备最高优先级
app.use(express.static(__dirname + '/public')); app.use(express.static(__dirname + '/files')); app.use(express.static(__dirname + '/uploads'));
为指定后缀名模板指定模板引擎回调。
默认状况,express使用require()
根据文件扩展名加载模板引擎。例如,若是渲染foo.jade
,express会内部执行如下操做,而且将回调进行缓存以提升性能。
app.engine('jade', require('jade').__express);
对于没有提供.__express
接口的引擎,须要定义映射,好比下面的例子
app.engine('html', require('ejs').renderFile);
在上面的例子中EJS提供.renderFile()
方法实现express所须要的(path, options, callback)
函数签名
映射逻辑到路由参数。例如当:user
在路由路径中出现的时候,能够经过为路由提供req.user
映射用户逻辑,或者针对参数进行验证。
注意:
app.param('id', function (req, res, next, id) { console.log('Called only once'); next(); }); app.get('/user/:id', function (req, res, next) { console.log('although this matches'); next(); }); app.get('/user/:id', function (req, res) { console.log('and this matches too'); res.end(); });
一下代码展现callback的用法,这很大程度上相似于中间件。代码根据参数尝试获取用户信息,若是成功,赋值给req.user
,失败调用next(err)
app.param('user', function (req, res, next, id) { User.find(id, function (err, user) { if (err) { next(err); } else if (user) { req.user = user; next(); } else { next(new Error('failed to load user')); } }); });
app.METHOD()
方法为express程序提供路由功能,其中METHOD为HTTP方法或加强方法,使用小写,例如app.get()
,app.post()
,app.patch()
。
express支持的方法有:
对于不能转化为合法JavaScript变量的方法,使用中括号访问法如:
app['m-search']('/', function () {});
能够设置多个回调,他们都相同,就像中间件同样工做。惟一的不一样时这些回调能够经过调用next('route')
跳过剩余的路由回调函数。这种方法能够用于执行条件路由,而后将控制传递给后续路由。
下面的代码展现最简单的路由定义。express将路径字符串转换为正则表达式,用于处理可能的请求。查询字符串不参与匹配。例如GET /
能够匹配GET /?name=tobi
app.get('/', function (req, res) { res.send('hello world'); });
可使用正则表达式对路径执行更多限制。例如如下代码能够匹配GET /commits/71dbb9c
,也能够匹配GET /commits/71dbb9c..4c084f9
app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){ var from = req.params[0]; var to = req.params[1] || 'HEAD'; res.send('commit range ' + from + '..' + to); });
能够传递多个回调。有助于中间件复用
app.get('/user/:id', user.load, function () { // ... });
若是有多个通用中间件,可使用route API中的all
var middleware = [loadForum, loadThread]; app.route('/forum/:fid/thread/:tid') .all(loadForum) .all(loadThread) .get(function () {}) .post(function () {});
这个方法相似于app.METHOD()
不一样的是,它能够匹配全部HTTP方法。
这个方法对于执行全局逻辑或者针对知足特定前缀的路径很是有用。例如若是你将如下路由放在全部路由定义以前,这就会强制全部路径访问都进行验证,而且自动加载用户。须要注意的是这种回调不该该终止请求,loadUser
执行操做以后调用next()
传递控制权给下一个中间件。
app.all('*', requireAuthentication, loadUser);
与下面等价
app.all('*', requireAuthentication); app.all('*', loadUser);
另外一种用法是对特定路径执行指定任务如:
app.all('/api/*', requireAuthentication);
返回一个路由实例,能够用户处理HTTP请求。使用app.route()
是避免屡次重复路径的推荐方法
var app = express(); app.route('/events') .all(function(req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware }) .get(function (req, res, next) { // res.json(...); }).post(function (req, res, next) { // may be add a new event.. });
用于设置提供给程序全部模板的变量。对于为模板提供辅助函数和app-level数据很是有用
app.locals.title = 'My App'; app.locals.strftime = require('strftime'); app.locals.email = 'me@myapp.com';
app.locals
是一个JavaScript对象。其属性会被设置为app局部变量
app.locals.title // => 'My App' app.locals.email // => 'me@myapp.com'
默认状况下。express值暴露一个app-level级别变量:settings
app.set('title', 'My App'); // use settings.title in a view
使用回调函数渲染view
,这是app-level的res.render()
app.render('email', function (err, html) { // ... }); app.render('email', {name: 'Tibi'}, function (err, html) { // ... });
在指定主机,端口绑定并监听。这个方法与node的http.Server#listen()相同
var express = require('express'); var app = express(); app.listen(3000);
express()
返回的app
是一个JavaScript Function
,能够传递给node的HTTP服务器做为回调来处理请求。这样就支持同时提供同一份代码支持HTTP和HTTPS版本的app。
var express = require('express'); var https= require('https'); var http = require('http'); var app = express(); http.createServer(app).listen(80); https.createServer(options, app).listen(443);
The app.listen() method is a convenience method for the following (if you wish to use HTTPS or provide both, use the technique above):
app.listen = function () { var server = http.createServer(this); return server.listen.apply(server, arguments); };
返回app路径
var app = express(); var blog = express(); var blogAdmin = express(); app.use('/blog', blog); blog.use('/admin', blogAdmin); console.log(app.path()); // '' console.log(blog.path()); // '/blog' console.log(blogAdmin.path()); // '/blog/admin'
当app具备复杂的mount时。这个方法将变得复杂,一次推荐使用req.baseUrl
来获取路径
返回子app mount的路径或者模式
var admin = express(); admin.get('/', function (req, res) { console.log(admin.mountpath); // /admin res.send('Admin Homepage'); }); app.use('/admin', admin); // mount the sub app
这个属性与req.baseUrl
相似,不一样的是app.mountpath
返回模式。
若是子app在多个路径mustache上进行了mount。app.mountpath
返回全部模式的列表
var admin = express(); admin.get('/', function (req, res) { console.log(admin.mountpath); // ['/adm*n', '/manager'] res.send('Admin Homepage'); }); var secret = express(); secret.get('/', function (req, res) { console.log(secret.mountpath); // /secr*t res.send('Admin Secret'); }); admin.use('/secr*t', secret); app.use(['/adm*n', '/manager'], admin);
在子app上监听mount
事件,当它被父app mount的时候触发。父app做为参数传递给回调函数。
var admin = express(); admin.on('mount', function (parent) { console.log('Admin Mounted'); console.log(parent); // refers to the parent app }); admin.get('/', function (req, res) { res.send('Admin Homepage'); }); app.use('/admin', admin);
这个属性是包含路由参数映射的对象,例如若是使用/user/:name
进行路由,req.params.name
上就设置了name
属性。req.params
默认值为{}
// GET /user/tj req.params.name // => 'tj'
当路由定义使用正则表达式时,捕获分组将出如今req.params[N]
数组对应元素,其中N是底n个捕获组。这个规则可使用在未知的匹配状况下,如/file/*
// GET /file/js/jquery.js req.params[0]; // => 'js/jquery.js'
保存query字符串解析后的数据,默认为{}
// GET /search?q=tobi+ferret req.query.q; // => 'tobi ferret' // GET /shoes?order=desc&shoe[color]=blue^shoe[type]=converse req.query.order // => 'desc' req.query.shoe.color // => 'blue' req.query.shoe.type // => 'converse'
包含请求体解析后数据的键值对。默认为undefined
,经过使用解析中间件渲染后获得对应值,常见中间件为body-parser和multer
下面的例子展现使用body-parser中间件来渲染req.body
var app = require('express')(); var bodyParser = require('body-parser'); var multer = require('multer'); app.use(bodyParser.json()); // for parsing application/json app.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-from-urlencoded app.use(multer()); // for parsing multipart/form-data app.post('/', function (req, res) { console.log(req.body); res.json(req.body); });
返回name
对应的参数值。
// ?name=tobi req.param('name'); // => 'tobi' // POST name=tobi req.param('name'); // => 'tobi' // user/tobi for /user/:name req.param('name'); // => 'tobi'
查询顺序以下:
能够指定defaultValue
,当参数名没有查找到的时候的默认值。
一般来讲,最好的方法是直接查询对应对象寻找值。
返回当前匹配的Route
app.get('/user/:id?', function userIdHandler(req, res) { console.log(req.route); res.send('GET'); });
输出结果相似:
{ path: '/user/:id?', stack: [ { handle: [Function: userIdHandler], name: 'userIdHandler', params: undefined, path: undefined, keys: [], regexp: /^\/?$/i, method: 'get' } ], methods: { get: true } }
须要cookieParser()
中间件来解析,生成用户代理包含cookie的键值对,默认为{}
。
// cookie: name=tj req.cookies.name // => 'tj'
参考cookie-parser更多信息。
使用cookieParser(secret)
渲染对象。 It contains signed cookies sent by the user-agent, unsigned and ready for use. Signed cookies reside in a different object to show developer intent; otherwise, a malicious attack could be placed on req.cookie values (which are easy to spoof). Note that signing a cookie does not make it "hidden" or encrypted; this simply prevents tampering (because the secret used to sign is private). If no signed cookies are sent, it defaults to {}.
// Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3 req.signedCookies.user // => "tobi"
参考cookie-parser查询更多信息。
读取不区分大小写的请求头首部。Referrer
和Referer
等价。
req.get('Content-Type'); // => 'text/plain' req.get('content-type'); // => 'text/plain' req.get('something'); // => undefined
此方法等价于req.header(field)
检查给定types
是否可接受(acceptable),当能够接受的时候返回最佳匹配项,若是不匹配,返回undefined
(此时可使用406 Not Acceptable
)。
type
值多是单个MIME type字符串如application/json
,或者扩展名json
,逗号分隔列表,或者数组。当列表或者数组设置时,函数返回最佳匹配项。
// Accept: text/html req.accepts('html'); // => 'html' // Accept: text/*, application/json req.accepts('html'); // => 'html' req.accepts('text/html'); // => 'text/html' req.accepts('json, text'); // => 'json' req.accepts('application/json'); // => 'application/json' // Accept: text/*, application/json req.accepts('image/png'); req.accepts('png'); // => undefined // Accept: text/*; q=.5, application/json req.accepts(['html', 'json']); req.accepts('html, json'); // => 'json'
查看accepts寻找更多信息。
检测制定charset是否可接受
检测制定lang是否可接受
检测指定encoding是否可接受
检测请求的Content-Type
是否匹配指定的MIME类型。
// with Content-Type: text/html; charset=utf-8 req.is('html'); req.is('text/html'); req.is('text/*'); // => true // when Content-Type is application/json req.is('json'); req.is('application/json'); req.is('application/*'); // => true req.is('html'); // => false
查看type-is寻找更多信息。
返回远程地址(或者trust proxy
激活时,upstream address)
req.ip; // => '127.0.0.1'
当trust proxy
为true
,解析X-Forwarded-For
获取ip地址列表,不然值为空数组。
返回请求URL的pathname
// example.com/users?sort=desc req.path // => '/users'
返回Host头包含的hostname值
// Host: example.com:3000 req.hostname // => example.com
检测请求是否新鲜。(Last-Modified
或者ETag
是否匹配)
req.fresh // => true
查看fresh寻找更多信息
检查请求是否stales
(Last-Modified
和ETag
是否匹配)
req.stale // => true
检查X-Requested-With
查看是否经过XMLHttpRequest
发送
req.xhr // => true
Return the protocol string "http" or "https" when requested with TLS. If the "trust proxy" setting is enabled, the "X-Forwarded-Proto" header field will be trusted. If you're running behind a reverse proxy that supplies https for you, this may be enabled.
req.protocol // => http
Check if a TLS connection is established. This is a short-hand for:
'https' == req.protocol;
返回包含子域名的数组
// Host: tobi.ferrets.example.com req.subdomains // => ['ferrets', 'tobi']
包含原始的请求url,req.url
包含的是去掉mount以后的路径
// GET /search?q=something req.originalUrl // => /search?q=something
This property refers to the URL path, on which a router instance was mounted.
var greet = express.Router(); greet.get('jp', function (req, res) { console.log(req.baseUrl); // /greet res.send('Konichiwa!'); }); app.use('/greet', greet); // load the router on '/greet'
若是mount使用的是模式。取值的时候获取的不是模式,是具体值
// load the router on '/gre+t' and '/hel{2}o' app.use(['/gre+t', '/hel{2}o'], greet);
请求/greet/jp
时,baseUrl为/greet,请求/hello/jp时baseUrl为/hello
req.baseUrl is similar to the mountpath property of the app object, except app.mountpath returns the matched path pattern(s).
node的res.statusCode
等价。用于设置HTTP响应状态码。
res.status(403).end(); res.status(400).send('Bad Request'); res.status(404).sendFile('/absolute/path/to/404.png');
设置响应头field值,或者传递对象设置多个field。
res.set('Content-Type', 'text/plain'); res.set({ 'Content-Type': 'text/plain', 'Content-Length': '123', 'ETag': '12345' });
与res.header(field, [value])
等价。
读取不区分大小写的响应头field
res.get('Content-Type'); // => 'text/plain'
设置cookiename
为value
。value
能够是字符串或者对象,path选项默认值为/
res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true }); res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
maxAge
选项用于设置expires
相对于当前时间的毫秒数。
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true });
也能够传递对象,最终序列为JSON,而后bodyParser()
自动解析
res.cookie('cart', { items: [1, 2, 3] }); res.cookie('cart', { items: [1, 2, 3], }, { maxAge: 900000 });
这种方法也能够设置signed cookie。只须要设置signed
选项。When given res.cookie() will use the secret passed to cookieParser(secret) to sign the value.
res.cookie('name', 'tobi', { signed: true });
而后经过访问req.signedCookie
获取信息
清除cookie name对应的值,path默认为/
res.cookie('name', 'tobi', { path: '/admin' }); res.clearCookie('name', { path: '/admin' });
express将设置的url字符串直接设置到
Location
头部,不进行验证。浏览器读取该头部并进行重定向
设置重定向url,状态码默认为302.
res.redirect('/foo/bar'); res.redirect('http://example.com'); res.redirect(301, 'http://example.com'); res.redirect('../login');
能够经过设置一个完整的url重定向到不一样的网站
res.redirect('http://google.com');
重定向能够相对于主机的跟路径,例如当前在http://example.com/admin/post/new
设置重定向url为/admin
会重定向到http://example.com/admin
res.redirect('/admin');
重定向能够相对于当前URL,从http://example.com/blog/admin/
(注意末尾的斜线)重定向到post/new
获得的最终地址为http://example.com/blog/admin/post/new
res.redirect('post/new');
若是没有后面的斜线http://example.com/blog/admin
那么post/new
会重定向到http://example.com/blog/post/new
若是对以上行为感到困惑,能够将路径看作目录和文件,这样就好理解了。
同时也支持路径的相对重定向。http://example.com/admin/post/new
能够经过如下方法重定向到http://example.com/admin/post
res.redirect('..');
也能够经过设置重定向实现后退效果。express使用referer进行设置,若是没有这个值,使用默认值/
res.redirect('back');
设置location报头
res.location('/foo/bar'); res.location('foo/bar'); res.location('http://example.com'); res.location('../login'); res.location('back');
这里的url规则和res.redirect()
相同
发送响应
res.send(new Buffer('whoop')); res.send({some: 'json'}); res.send('<p>some html</p>'); res.status(404).send('sorry, can not find it'); res.status(500).send({error: 'something blew up'});
这个函数对于非流式响应会自动设置Content-Length
(没有在其余地方设置时)而且提供HTTP缓存支持。
发送Buffer时,若是其余地方没有设置,Content-Type
设置为application/octet-stream
res.set('Content-Type', 'text/html'); res.send(new Buffer('<p>some html</p>'));
返回字符串时Content-Type
设置为text/html
res.send('<p>some html</p>');
返回数组或者对象时,默认设置JSON
res.send({user: 'tobi'}); res.send([1, 2, 3]);
返回json响应,参数为对象或者数组时与res.send()
等价。对于其余状况下使用这个方法能够明确设置对应报头。
res.json(null); res.json({user: 'tobi'}); res.status(500).json({error: 'message'});
返回一个JSONP响应。使用方法与res.json()
相同,不一样的是返回结果会加上回调函数名字以支持JSONP,默认函数名为callback
,能够经过app.set('jsonp callback name', 'aaa')
设置与请求约定的回调名参数。
res.jsonp(null); res.jsonp({ user: 'tobi' }); res.status(500) .jsonp( { error: 'message' });
// ?callback=foo res.jsonp({user: 'tobi'}); // => foo({'user': 'tobi'}) app.set('jsonp callback name', 'cb'); // ?cb=foo res.status(500).jsonp({error: 'message'}); // => foo({'error': 'message'})
设置Content-Type
,根据后缀名查询对应值,若是包含/
那直接设置为对应结果。
res.type('.html'); res.type('html'); res.type('json'); res.type('application/json'); res.type('png');
太长不想翻了查看res.format(object)
设置Content-Disposition
报头为attachment
,若是指定了filename,Content-Type
也会根据扩展名进行设置,同时Content-Disposition
的filename=参数也会设置。
res.attachment(); // Content-Disposition: attachment res.attachment('path/to/logo.png'); // Content-Disposition: attachment; filename="logo.png" // Content-Type: image/png
res.sendFile
须要express版本高于4.8.0
设置HTTP状态码而且将响应体设置为对应描述字符
res.sendStatus(200); // equivalent to res.status(200.send('OK')) res.sendStatus(403); // equivalent to res.status(403).send('Forbidden'); res.sendStatus(404); // equivalent to res.status(404).send('Not Found')
若是设置了位置状态码。HTTP状态码也会设置为指定值,描述字符串也于指定值相同
res.sendStatus(2000); // res.status(2000).send('2000');
设置下载res.download()
设置Link
响应头
res.links({ next: 'http://api.example.com/users?page=2', last: 'http://api.example.com/users?page=5' });
结果
Link: <http://api.example.com/users?page=2>; rel="next", <http://api.example.com/users?page=5>; rel="last"
设置本地变量,只能在请求/响应周期内的视图渲染可见。具体操做与app.locals相同
这个对象在暴露请求级别信息时很是有用,如请求路径名,验证信息,用户设置等。
app.use(function (req, res, next) { res.locals.user = req.user; res.locals.authenticated = !req.user.anonymous; next(); });
渲染指定的视图,出现错误时内部调用next(err)
。提供的回调函数会产地错误和渲染后的字符串做为参数,此时不会自动发送响应。
res.render('index', function (err, html) { // ... }); res.render('user', {name: 'Tobi'}, function (err, html) { // ... });
在响应报头没有设置的状况下添加该报头。
res.vary('User-Agent').render('docs');
继承自node的http.serverResponse
,用于结束响应。惟一推荐的用处是不发送数据而快速结束响应。若是想要发送数据,推荐使用res.send()
,res.json()
等。
res.end(); res.status(404).end();
用于检测HTTP投是否已经发送
app.get('/', function (req, res) { console.log(res.headersSent); // false res.send('OK'); console.log(res.headersSent); // true });
路由是Router或者中间件的实例。路由能够看作微型应用,智能用于执行中间件和路由函数。每个express应用程序有一个内置的app router。
路由表现上与中间件类似,能够在app或者其余路由上经过.user()
进行使用。
一下方法建立一个路由:
var router = express.Router([options]);
可选的选项能够修改路由行为
caseSensitive
强制区分大小写。默认不区分,/Foo
与/foo
同样strict
激活严格路由,默认状况下/foo
和/foo/
认为相同mergeParams
,默认值为false
确保来自父路由的req.params
获得保留。若是父路由和子路由有参数冲突。子路由参数优先级更高。// invoked for any requrests passed to this router router.use(function (req, res, next) { // .. some logic here.. like any other middleware next(); }); // will handle any request that ends in /events // depends on where the router is user() router.get('/events', function (req, res, next) { // .. });
能够将路由设置到对应的url上针对特定url进行响应
// only requests to /calendar/* will be sent to our router app.use('/calendar', router);
相似于app.use()
使用中间件处理请求。mount路径默认为'/'
var express = require('express'); var app = express(); var router = express.Router(); // simple logger for this router's requests // all requests to this router will first hit this middleware router.use(function (req, ers, next) { router.log('%s % %s', req.method, req.url, req.path); next(); }); // this will only be invoked if the path starts with /bar front the mount point router.use('/bar', function (req, res, next) { // maybe some additional operation next(); }); // always invoked router.use(function (req, res, next) { res.send('Hello World'); }); app.use('/foo', router); app.listen(3000);
mount路径在传递给中间件时会自动去掉。这样产生的效果是中间件能够不关心具体路径前缀。
router.use()
注册中间件的顺序很是重要,全部中间件按照顺序调用。
var logger = require('morgan'); router.use(logger()); router.use(express.static(__dirname + '/public')); router.use(function (req, res) { res.send('Hello'); });
假设想要忽略静态文件的log,可是后续的中间件须要日志。能够将静态文件中间件放到前面。
router.use(express.static(__dirname + '/public')); router.use(logger()); router.use(function (req, res) { res.send('Hello'); });
另外一个使用场景是在多个目录提供静态文件访问。express会按照顺序遍历目录来提供文件。
app.use(express.static(__dirname + '/public')); app.use(express.static(__dirname + '/files'));
为路由参数映射逻辑。例如:user
出如今路由路径的时候能够映射用户加载和验证操做。
在一个请求响应周期中回调函数只执行一次,即便参数匹配多个路由
app.param('id', function (req, res, next, id) { console.log('Called only once'); next(); }); app.get('/user/:id', function (req, res, next) { console.log('although this matches'); next(); }); app.get('/user/:id', function (req, res) { console.log('and thsi matches too'); res.end(); });
回调函数与中间件相似,可是多了一个参数,用于对应请求实际参数。也能够从req.params
获取,能够加载用户以后添加到req.user
进行保存
返回对应路径的一个路由实例,而后针对路由设置不一样HTTP方法的逻辑。这样能够有效避免屡次对同一个路径重复添加不一样方法的中间件。
var router = express.Router(); router.param('user_id', function (req, res, next, id) { // sample user, would actually fetch from DB, etc.. req.user = { id: id, name: 'tj' }; next(); }); router.route('/users/:user_id') .all(function (req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware }) .get(function (req, res, next) { res.json(req.user); }) .put(function (req, res, next) { // just an example of maybe updating the user req.user.name = req.params.name; // save user ... etc res.json(req.user); }) .post(function (req, res, next) { next(new Error('not implemented')); }) .delete(function (req, res, next) { next(new Error('not implemented')); });
经过以上方法能够重用/users/:user_id
来添加多个HTTP方法响应
为路由制定HTTP方法响应处理。
为路由制定响应全部HTTP方法的中间件
这个问题没有肯定的答案。取决于你程序的规模和开发团队。为了达到尽可能的灵活,Express不对结构进行假设。
路由和其余程序相关的逻辑能够根据你的喜爱放到任意目录和文件中。能够查看下面的例子来得到灵感:
一样存在一些第三方扩展,用于简化模式
express没有数据库的概念。这彻底交给第三方模块,这样容许你与任何数据库交互。
能够查看LoopBack
Express没有限制,你可使用你但愿的任意方法。这里是一个简单的用户名/密码模式:查看例子
express支持全部符合(path, locals, callback)
签名的模板引擎。若是想规格化模板引擎接口和缓存,查看consolidate.js。
一般能够屡次使用任意的中间件。以下代码中,若是请求GET /js/jquery.js
在./public/js/jquery.js
没有找到,它继续尝试./files/js/jquery.js
app.use(express.static('public')); app.use(express.static('files'));
connect的"mounting"特性容许你定义路径前缀。这样就好像前缀字符串没有出如今路径中同样。假设你须要访问GET /files/js/jquery.js
。你能够在/files
目录mount中间件,暴露/js/jquery.js
做为url。代码以下:
app.use('/files', express.static('public'));
express中,404不是错误的结果。所以,错误处理中间件不会捕获404.由于404代表还有工做没有完成,也就是说,express执行了全部中间件/路由,可是没有找到对应的响应处理。你须要在末尾添加一个中间件来处理404:
app.use(function (req, res, next) { res.send(404,, 'Sorry can not find that'); });
像定义其余中间件同样定义错误处理程序。惟一的区别是这个程序指望参数为四个(err, req, res, next)
,查看错误处理寻找更多信息。
app.use(function (err, req, res, next) { console.error(err.stack); res.send(500, 'something broke'); });