路由详解

路由处理器执行顺序

编码的顺序决定了路由处理器执行顺序。

app.get(“/test”,function(req,res,next){
    console.log(“router A”);
    next();
})

app.get(“/test”,function(req,res,next){
    console.log(“router B”);
    next();
})

这个顺序编码,浏览器访问/test 时,终端会打印出:

router A
router B

无论调用 app.use 、app.all、app.get、app.post 都是一样的,按编码顺序执行。还需强调一点,这个顺序执行的前提是,一致的路由;这个路由的一致,体现在相同的方法和相同的路径。

app.get(“/test”,function(req,res,next){
    console.log(“router A”);
    next();
})

app.post(“/test”,function(req,res,next){
    console.log(“router B”);
    next();
})

这样就没有顺序可言,因为顺序的前提必须要是一致的路由。next 这个方法调用之前,不会调用之后的路由处理器。

app.get(“/test”,function(req,res,next){
    console.log(“router A”);
})

app.get(“/test”,function(req,res,next){
    console.log(“router B”);
    next();
})

这个只会打印出 router A ,因为第一个路由处理器没有调用next() 方法。

始终我们在浏览器都没看到响应,是因为从来没调用过响应方法,比如  res.send 或 res.render 方法。

app.get(“/test”,function(req,res,next){
    console.log(“router A”);
    next()
})

app.get(“/test”,function(req,res,next){
    console.log(“router B”);
    res.send(“router B”);
})

这样浏览器会显示出 router B 字符串,我们发现第二个路由处理器没有调用next ,因为调用next() 的目的是执行之后的路由处理器,如果后面没有就无需调用。另外要注意一点,调用了任何响应方法,都表示响应的任务完成。比如下面代码:

app.get(“/test”,function(req,res,next){
    console.log(“router A”);
            res.send(“router A”)
    next();
})

app.get(“/test”,function(req,res,next){
    console.log(“router B”);
    res.send(“router B”);
    next();
})

app.get(“/test”,function(req,res,next){
    console.log(“router C”);
    res.send(“router C”);
    next();
})

调用后浏览器只会显示出router A,因为 “ 调用了任何响应方法,都表示响应的任务完成。 ” ,当然第二个路由响应器还是会执行,只不过所有的响应方法都不再起作用。

而其他代码还会起作用,终端会打印出:

router A
router B
router C

app.all

app.all 表示响应所有http方法。

app.all(“/test”,function(req,res,next){
    console.log(“app.all method”);
    next();
})

app.get("/test",function(req,res,next){
    res.send("app.get method")
})

app.post("/test",function(req,res,next){
    res.send("app.post method")
})

无论我们通过 get 或 post http方法访问,终端都会打印app.all method。 用 app.use 代替 app.all 效果也一样,但use不支持多路由处理器,比如下面代码:

app.all("/test",function(req,res,next){
    console.log("app.all method");
    next();
},function(req,res,next){
    console.log("app 2");
    next();
})

app.get("/test",function(req,res,next){
    res.send("app.get method")
})

app.post("/test",function(req,res,next){
    res.send("app.post method")
})

终端会打印出

app.all method
app 2

但用use代替all方法,只打印出

app.all method

app.all / app.get / app.post 方法支持多路由处理器链   ,app.use 方法不支持,但它支持express.Router的实例,下面就来探讨 express.Router 类。

express.Router

一个express.Router 对象,可以理解为是一个独立的 mini app,它也可以调用use方法加入中间件,但只作用于自身。

通过 var router  = express.Router([可选参数]) 创建一个路由器对象。

可选参数对象有两个可选值,caseSensitive 区分大小写,例如/Foo 和 /foo ,默认false关闭。strict 严格路由,例如 /foo 和 /foo/ ,默认false关闭。

它是个mini app,所以它的行为方式和app一致,下面是个例子:

var router = express.Router();
router.get("/abc",function(req,res,next){
    res.send("abc abc");
})

app.use("/user",router);

通过 /user/abc 可以访问,通过app.use方法加入到应用程序的路由链上。

比如我们系统中有一些子系统,user 、book和 forum:

app.use("/user",require(“./user”));
app.use("/book",require(“./user”));
app.use("/forum",require(“./user”));

模块化很方便,便于管理和维护。既然它是mini app,它自身的use方法也可以应用其他router。例如:

var sub = express.Router();
sub.get("/sub",function(req,res,next){
    res.send("sub is me");
})

var router = express.Router();
router.use("/abc",sub);

app.use("/user",router);

通过 /user/abc/sub 可以访问到,实际开发不会这么写,这里只是举个例子。

另外, 无论app还是mini app的Router对象,都支持下面的糖衣写法:

// 也可以用于 app.route
router.route('/users/:user_id')
.all(function(req, res, next) {
})

.get(function(req, res, next) {
 },function(req,res,next){})

.put(function(req, res, next) {

 })
.post(function(req, res, next) {

})
.delete(function(req, res, next) {

})

泛式路由

/user/id0001 这是明确路由,泛式路由在“web框架开发实战”部分已经介绍过了,这里不做说明。

/user/:id 和  /user/* 都属于泛式路由,在express如何获得路由参数的呢?这个放在下一节Request请求来讲。

这个部分我们要探讨express泛式路由的写法。

  • /user/:id 这个路由通过,user/id 都可访问到。

  • /user/:id? 这个路由通过,/useruser/id 都可访问到。

  • /user/* 这个路由通过,/useruser/id 都可访问到。

  • /user/*/id?  这个路由通过,/useruser/id001user/xxx/id001都可访问到。

  • /user/*/:id  这个路由通过, user/id001user/xxx/id001都可访问到。

这两个写法的差异在于,定义了:id 就可通过 req.params.id 得到改参数。

通过正则可以写复杂些的路由器。例如:

router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
  var from = req.params[0];
  var to = req.params[1] || 'HEAD';
  res.send('commit range ' + from + '..' + to);
});

通过 /commits/71dbb9c..4c084f9 可以访问到,params[0] 对应的是 71dbb9c , params[1] 对应的是 4c084f9