Connect是一个框架,它使用被称为中间件的模块化组件,以可重用的方式实现web程序的逻辑。在Connect中,中间件组件是一个函数,它拦截HTTP服务器提供的请求和响应,执行逻辑,然后,或者结束响应,或者把它传递给下一个中间件组件。Connect用分配器把中间件“连接”在一起。
(Express构建在Connect之上的更高层的框架,添加了更多糖衣)
- ex1 最小的Connect程序
var connect = require("connect");
var app = connect();
app.listen(3000);
所有HTTP请求结果:404 Not Found
Connect 工作机制
-
Connect分配器会依次调用所有附着的中间件组件,直至其中一个组件决定响应该请求,如果知道中间件组件列表末尾还是没有组件决定响应,程序会返回404作为响应。(ex1这个裸程序没有中间件组件,故会返回404 Not Found状态码响应他接收到的所有HTTP请求。)
-
Connect中,中间件组件是一个JS函数,接收3个参数
a. 请求对象(req)
b. 响应对象(res)
c. 通常会命名为next的参数:一个回调函数,表明当前中间件已经执行完毕,可以执行下一个中间件组件
- ex2 简单的 hello world + 日志log
var connect = require("connect");
var app = connect()
.use(logger)
.use(hello)
.listen(3000);
function logger(req, res, next) {
console.log(req.method + ' ' + req.url);
next(); //执行下一个中间件
}
function hello(req, res) {
res.setHeader('Content-type', 'text/plain');
res.end("hello world");
}
- ex3 中间件的顺序问题
var connect = require("connect");
var app = connect()
.use(hello)
.use(logger)
.listen(3000);
function logger(req, res, next) {
console.log(req.method + ' ' + req.url);
next(); //执行下一个中间件
}
function hello(req, res) {
res.setHeader('Content-type', 'text/plain');
res.end("hello world");
}
在ex3中,先执行了hello的中间件,响应了HTTP的请求,同时没有调用next(),控制权不会回到分配器去调用下一个中间件,所以logger中间件不会调用。
注意:当一个中间件组件不调用next()时,命令链中的其他中间件都不会被调用。
挂载
可以给中间件或整个程序定义一个路径前缀。通过使用挂载,可以像在根层次中编写中间件一样(/根req.url),并且可以不修改代码就将他放在任何路径前缀上。挂载还将只对路径前缀内的请求调用中间件或程序。
- ex4 验证用户和用户管理区
var connect = require("connect");
var app = connect()
.use(logger)
.use('/admin',restrict) //restrict组件用于验证用户,只在/admin的路径上调用
.use('/admin',admin) //admin组件用于管理用户,只在/admin的路径上调用
.listen(3000);
创建可配置中间件——闭包
- ex5 可配置中间件基本结构
function setup(options){
return function(req, res, next){
//中间件逻辑
}
}
app.use(setup({some: 'options'}));
在ex2中有一个简单的logger中间件,在代码里面已经写死了将会打印出 req.method 、req.url,不利于代码的重用,无法应对需求的变化
function logger(req, res, next) {
console.log(req.method + ' ' + req.url);
next(); //执行下一个中间件
}
- ex6 可配置的logger中间件
function setup(format){
var regexp = /:(\w+)/g;
return function logger(req, res, next){
var str = format.replace(regexp, function(match, property) {
/*
当string.replace()的第二个参数为字符串时,则替换匹配到的第一个字符串
当string.replace()的第二个参数为函数时,则替换每一个匹配到的字符串
有4个参数
第一个参数(match)为正则表达式捕获的字符串,如:method、:url
第二个参数(property)为捕获括号所捕获的字符串,如method、url
第三个参数为正则表达式匹配到的每段字符的第一个字符的索引
第四个参数为匹配的字符串主体
*/
return req[property];
});
console.log(str);
next();
}
}
module.exports = setup;
- ex7 简单的路由中间件
./middleware/router.js
var parse = require('url').parse;
module.exports = function route(obj) {
return function(req, res, next) {
if (!obj[req.method]){
next();
return;
}
var routes = obj[req.method];
var url = parse(req.url);
var paths = object.Keys(routes);
for(var i = 0; i < paths.length; i++){
var path = paths[i]; // /user/:id
var fn = routes[path];
// /user/:id -> /user/([^\\/]+)
path = path.replace('/\//g','\\/').replace('/:(\w+)/g','([^\\/]+)');
var re = new RegExp('^' + path + '$'); // ^/user/([^\\/]+)$
var captures = url.pathname.match(re); // ['/user/12',12,...]
if(captures){
var args = [req, res].concat(captures.slice(1)); //concat 数组连接; slice(start, end)返回数组start到end的元素的数组
fn.apply(null, args);
return;
}
}
next()
}
};
使用 router 中间件
var connect = require("connect");
var router = require('./middleware/router');
var routes = {
GET:{
'/users':function(req, res) {
res.end("Mike","Jay","Kim");
}
'/user/:id':function(req, res, id) {
res.end('user ' + id);
}
},
DELETE:{
'/user/:id':function(req, res, id) {
res.end('deleted user ' + id);
}
}
}
connect()
.use(router(routes))
.listen(3000);
处理程序错误的中间件
- ex8
function erorHandler() {
var env = process.env.NODE_ENV || 'development'; //在不同的服务器环境中切换,比如生产和开发环境
return function(err, req, res, next) {
res.statueCode = 500;
switch(env){
case 'development':
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify(err));
break;
default:
res.end('server error');
}
}
}









网友评论