Section-3 Koa 框架的路由与 RESTful API
Lesson-1 路由简介
什么是路由
- 决定了不同 URL 是如何被不同地执行
- 在Koa中,是一个中间件
如果没有路由,会怎样?
- 所有请求都做了相同的事
- 所有请求都会返回相同的结果
运行前面编写的代码,浏览器访问以下地址
localhost:3000
- 页面呈现hello world
localhost:3000/users
- 页面呈现hello world
localhost:3000/users/userid
- 页面呈现hello world
上面就是因为没有路由,所以全都做了相同的事,返回相同的结果
推荐一款工具 postman ,可以方便快捷的实现接口请求与获取返回值(当然你依旧可以选择在浏览器控制台里,或新建一个html文件,来执行fetch请求)
路由存在的意义
- 处理不同的URL
- 处理不同的HTTP方法
- 解析URL上的参数
Lesson-2 自己编写一个 Koa 路由中间件
操作步骤
- 处理不同的 URL
- 处理不同的 HTTP 方法
- 解析 URL 上的参数
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa(); // 实例化koa
// 接受一个函数,该函数为一个中间件
app.use(async (ctx) => {
switch (ctx.url) {
case '/':
ctx.body = "这是主页";
break;
case '/user':
ctx.body = '这是用户页';
break;
default:
ctx.status = 404; // 直接设置404页面会简单的出现默认的 Not Found
ctx.body = "page not found!" // 这是相当于实现一个自定义的404页面
break;
};
});
app.listen(3000, () => {
console.log(`start server...`);
});
上面这段代码就实现了一个手写的简单路由,代码很简单应该不用解释吧
为什么上面可以直接使用ctx.url,而不是request.url?因为文档里可以看到下面这部分,这是因为koa底层对 Request 和 Response 做了处理,所以直接使用上下文就可以拿到请求地址
![](https://img.haomeiwen.com/i13936697/5cea4a9f8a6248cd.png)
接下来继续完善,对user页里对请求方法进行判断
case '/user':
if (ctx.method === 'GET') {
ctx.body = '这是用户列表页';
} else if (ctx.method === 'POST') {
ctx.body = '创建用户页';
} else {
ctx.status = 405; // 直接设置405状态,页面会简单的出现默认的 Method Not Allowed
ctx.body = '不允许该方法'; // 这是相当于实现一个自定义的405页面
}
![](https://img.haomeiwen.com/i13936697/694edd7c275fd1bd.png)
继续完善,获取详细用户,所以最终结果如下
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa(); // 实例化koa
// 接受一个函数,该函数为一个中间件
app.use(async (ctx) => {
if (ctx.url === '/') {
ctx.body = "这是主页";
} else if (ctx.url === '/user') {
if (ctx.method === 'GET') {
ctx.body = '这是用户列表页';
} else if (method === 'POST') {
ctx.body = '创建用户页';
} else {
ctx.status = 405; // 直接设置405状态,页面会简单的出现默认的 Method Not Allowed
ctx.body = '不允许该方法'; // 这是相当于实现一个自定义的405页面
}
} else if (ctx.url.match(/\/user\/\w+/)) {
const userId = ctx.url.match(/\/user\/(\w+)/)[1];
ctx.body = `用户${userId}`;
} else {
ctx.status = 404; // 直接设置404状态,页面会简单的出现默认的 Not Found
ctx.body = "page not found!" // 这是相当于实现一个自定义的404页面
}
});
app.listen(3000, () => {
console.log(`start server...`);
});
当然自己写这种中间件不仅不完善,而且问题也不少,所以应该尽可能使用社区中的中间件
Lesson-3 使用 koa-router 实现路由
操作步骤
- 更优雅地实现路由基本功能
- 演示一些高级路由功能,如前缀、多中间件
由于前面我已经一并把router下载了,所以直接用就行了,npm上也已经罗列出支持的方法了,内容比较少跟容易理解,这里就直接写代码了
koa-router NPM地址
// index.js
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa(); // 实例化koa
const router = new Router(); // 实例化路由
router.get('/', (ctx) => {
ctx.body = "这是主页"
});
router.get('/user', (ctx) => {
ctx.body = "这是用户列表"
});
router.post('/user', (ctx) => {
ctx.body = "创建用户页"
});
router.get('/user/:id', (ctx) => {
ctx.body = `用户${ctx.params.id}`;
});
// 启动路由
app.use(router.routes());
app.listen(3000, () => {
console.log(`start server...`);
});
接下来尝试一些高级点的功能,添加前缀,使用前缀优点一个是减少代码量,一个是方便统一管理
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa(); // 实例化koa
const router = new Router();
// 设置前缀,这样下面就不用每次都写/user了
const userRouter = new Router({prefix: '/user'});
router.get('/', (ctx) => {
ctx.body = "这是主页"
});
userRouter.get('/', (ctx) => {
ctx.body = "这是用户列表"
});
userRouter.post('/', (ctx) => {
ctx.body = "创建用户页"
});
userRouter.get('/:id', (ctx) => {
ctx.body = `用户${ctx.params.id}`;
});
// 启动路由
app.use(router.routes());
app.use(userRouter.routes());
app.listen(3000, () => {
console.log(`start server...`);
});
尝试一下另一种用法,做一个模拟的用户拦截,如果请求游客信息,将会被拦截
// 模拟的用户安全,作为安全层
const auth = async (ctx, next) => {
// 异常前置,如果url地址中是/user/visitor,将会被拦截
if(ctx.params.id === 'visitor') {
ctx.throw(401); // 注意别写字符串的401,否则报的是500
}
await next();
}
userRouter.get('/:id', auth, (ctx) => {
ctx.body = `用户${ctx.params.id}`;
});
请求地址:localhost:3000/user/visitor,将会出现 Unauthorized,状态401(未授权),证明被拦截
请求地址:localhost:3000/user/master,将会出现 用户master
![](https://img.haomeiwen.com/i13936697/e1d2335f3ed42ee3.png)
Lesson-4 HTTP options 方法的作用是什么
为何要了解 options 方法的作用
- 这是一道面试题
- 帮助理解 koa-router 的 allowedMethods 的作用
比如以下使用 options 方法请求 http://example.org ,就会罗列出该 URL 所支持的方法请求
![](https://img.haomeiwen.com/i13936697/9c6b57a3ff028b4a.png)
HTTP options 方法的作用
- 检测服务器所支持的请求方法
- CORS 中的预检请求(检查文章的时候还是觉得找篇文章科普一下,否则我只说跨域,可能还真有小白不知道我在说什么…)
allowedMethods 的作用
- 响应 options 方法,告诉它所支持的请求方法
- 相应的返回405(不允许)和501(没实现)
没加 allowedMethods() 丰富请求头的情况下,使用options方法请求 URL,那么将会返回404
![](https://img.haomeiwen.com/i13936697/ff8e22543449cbb2.png)
// index.js,给userRouter添加allowedMethods方法后
app.use(userRouter.allowedMethods());
![](https://img.haomeiwen.com/i13936697/e4026244c70d1a35.png)
而当我们使用未被允许的方法来请求时,这个时候将会返回405(不允许),而这些就是使用router.allowedMethods()丰富请求头带来的好处
![](https://img.haomeiwen.com/i13936697/832c32c575c7cc72.png)
而当我们使用一些生僻的方法的时候,比如使用link方法
![](https://img.haomeiwen.com/i13936697/5be2cb9b40b11178.png)
这是为什么呢?为什么同样是使用不允许的方法,而link方法就会返回501呢?
这是因为Koa只实现了一些常用的方法,比如 GET / POST / PUT / PATCH / DELETE / HEAD / OPTIONS,而使用了一些生僻方法的时候,koa实际上是有能力实现的,但是它并未实现,那么这个时候就会返回501
Lesson-5 RESTful API最佳实践 - 增删改查应该返回什么响应?
操作步骤
- 实现增删改查
- 返回正确的响应
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa(); // 实例化koa
const router = new Router();
// 设置前缀,这样下面就不用每次都写/user了
const userRouter = new Router({prefix: '/user'});
router.get('/', (ctx) => {
ctx.body = "这是主页"
});
// post(增)、delete(删)、put(改)、get(查)
// 获取用户列表
userRouter.get('/', (ctx) => {
// 应该返回查询的数据
ctx.body = [
{name: '韩梅梅'},
{name: '李蕾'}
];
});
// 获取特定用户
userRouter.get('/:id', (ctx) => {
ctx.body = `用户${ctx.params.id}`;
});
// 增加/新建用户
userRouter.post('/', (ctx) => {
// 新建用户,应该返回新建的对象
ctx.body = {name: '韩梅梅'};
});
// 修改用户
userRouter.put('/', (ctx) => {
ctx.body = {name: '涵美眉'};
});
// 删除用户
userRouter.delete('/', (ctx) => {
ctx.status = 204; // 没有内容,但是成功了
});
// 启动路由
app.use(router.routes());
app.use(userRouter.routes());
app.use(userRouter.allowedMethods());
app.listen(3000, () => {
console.log(`start server...`);
});