
2.2.2 路由详解
koa-router非常强大,提供了多种路由解析方式。本节将从路由常见用法、路由与中间件的关系、HTTP动词等方面详细解读路由相关知识。
➘ 路由常见用法
下面看一下路由的常见写法。我们首先创建router.verb()方法,这里的verb指的是HTTP动词,比如router.get()或router.post()等。然后匹配URL模式,通过router.verb()的回调函数完成具体的业务逻辑。示例如下。

路由的路径会被转译成正则表达式,用于匹配请求。匹配请求时只和路径有关,Query String是不会被考虑在内的,这一点在定义路由时要搞清楚。例如GET/会匹配下面的路由,也会匹配GET/?name=i5ting。

具名路由指的是路径包含占位符:xxx的。比如'/users/:id',当URL是'/users/123'时,id就是123,即:id会被从正则表达式里提取出来。
具名路由参数通过ctx.params来获取,代码如下,这一点和Express里的req.params是一样的。

通过这种方式来表示URL里可变内容的状态是非常好的。
正则表达式是非常强大的,其实koa-router的路由也可以用正则表达式来处理,例如下面这段代码会匹配—GET/commits/71dbb9c”,包括—GET/commits/71dbb9c..4c084f9”。

还有一种常见的路由是嵌套路由,即不同实体之间是一对多的关系。为了表示这种关系,可以在URL中表述该状态,比如一个论坛中有多个帖子,那么该如何表示出这个论坛的所有帖子呢?示例代码如下。

总结一下路由的4种常见形式,对比如下。
○ 普通路由:'/users'。
○ 具名路由:'/users/:id'。
○ 正则路由:/^\/commits\/(\w+)(?:\.\.(\w+))?$/。
○ 嵌套路由:上面多种路由形式的组合。
➘ 路由也是中间件
路由的定义方法如下。

来看一个简单的路由示例,代码如下。

在这个例子里,第一个中间件根据ID查询到的用户,先被绑定到了ctx对象上,然后交给了下一个中间件进行处理。接着,第二个中间件根据第一个中间件产生的ctx.user完成某些内容,最终决定返回何值。
那么,由多种中间件组成的路由可以完成哪些事情呢?具体如下。
○ 解耦代码,将代码拆分得更细致。
○ 实现模块化,通过组装中间件来完成逻辑。
○ 实现权限类、日志类的AOP(面向切面编程)。
○ 实现与Hook(钩子代码)类似的抽象。
➘ HTTP动词
以前大家都认为管理信息系统(MIS)就是要实现增、删、改、查(CRUD)操作,认为这种操作没有什么技术含量,其实真正能把CRUD写明白也不是一件容易的事。
这里给出一个基于RESTful风格实现7个路由的例子。

其中4个路由分别了实现增、删、改、查(CRUD)操作,如下。
○ GET/users[/]=>user.list()
○ POST/users[/]=>user.create()
○ PATCH/users/:id=>user.update()
○ DELETE/users/:id=>user.destroy()
另外3个路由是用于页面渲染的,如下。
○ GET/users/new=>user.new()
○ GET/users/:id=>user.show()
○ GET/users/:id/edit=>user.edit()
我们先来看一下CRUD对应的请求方法,具体如下。
○ get:用于请求资源。
○ post:用于创建资源。
○ patch:用于更新资源。
○ delete:用于删除资源。
➘ 其他方法
我们来介绍一个router.all方法,这个方法和router.get、router.put、router.post、router.patch、router.delete是一样的,唯一不同的是,无论是GET、PUT、POST、PATCH、DELETE中的哪个动词,这个方法都能响应。router.all能匹配所有的HTTP动词,简单示例如下。

除了匹配所有动词,router.all几乎无其他优势,因为GET和POST处理请求的方法是不同的,所以router.all里的逻辑无法兼容GET或POST,它只能完成与表单无关的功能。
router.all的常见使用场景如下。
○ 同时支持多种HTTP动词。
○ 快速实现功能,不需要深究语义。比如上面代码里的重定向功能,任何HTTP动词都可以访问。
注意,真正的业务代码中不推荐使用这种方法。