使用 koa 启动一个服务
const Koa = require('koa');
const app = new Koa();
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
kao 的 ctx 对象, 里面的 req, res, request, response 的区别
const Koa = require('koa');
const app = new Koa();
// ctx 就是koa的上下文, 它里面封装了node的response和request对象
app.use((ctx, next) => {
// 表示node的request对象
const request = ctx.req;
// 表示node的response对象
const response = ctx.res;
// koa 的 request对象
const koaRequest = ctx.request;
// koa 的 response对象
const koaResponse = ctx.response;
// 设置响应 Content-Type
koaResponse.type = 'text/plain;charset=utf-8';
// 将内容之返回给客户端
koaResponse.body = 'hello world!';
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
koa 的 ctx 对象里面常用的属性, ctx.request.url, ctx.request.query
const Koa = require('koa');
const app = new Koa();
// ctx 就是koa的上下文, 它里面封装了node的response和request对象
app.use((ctx, next) => {
// koa 的 request对象
const koaRequest = ctx.request;
// koa 的 response对象
const koaResponse = ctx.response;
// 设置响应头里的 Content-Type 类型
koaResponse.type = 'application/json;charset=utf-8';
// 将内容之返回给客户端
koaResponse.body = {
url: koaRequest.url, // 请求的url
query: koaRequest.query //请求url所带的参数
};
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
使用 request 来处理路由
通过下面的几个api, 我们就可以知道请求的方法, url的内容, 和所带的参数, 这样我们便可以通过不同的路由和方法, 来处理不同的请求
const Koa = require('koa');
const app = new Koa();
// ctx 就是koa的上下文, 它里面封装了node的response和request对象
app.use((ctx, next) => {
const { request, response } = ctx;
// 获取到请求的方法
const method = request.method;
// 获取到请求的url
const url = request.url;
// 获取到url上面的参数
const query = request.query;
if (method === 'GET') {
if (url !== '/admin') {
response.type = 'application/json';
response.body = {
method,
url,
query,
message: '请访问/admin'
};
} else {
response.type = 'text/plain';
response.body = '欢迎登陆';
}
}
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
koa 的中间件机制
在 koa 里, 有个很重要的概念, 就是中间件, 每个中间件都是一个函数, 从官方的说法来看, 就像是一个洋葱模型, 从最外面往里走, 然后在回到最外层, 图形如下:
换句话讲, 就是你想要执行你后面的函数, 那么必须让上一个函数同意, 如果上一个函数不同意的话, 那么下一个中间件将不会执行,而在被app.use(fun)后, fun的参数里面将会有两个值, 一个是ctx, 一个 是next, ctx是koa的上下文, next就可以理解为让下一个函数是否执行的开关, 下面我将用代码来演示一下koa的中间件.
const Koa = require('koa');
const app = new Koa();
// 第一个中间件
app.use(async (ctx, next) => {
// 不解析 /favicon.ico 路由
if (ctx.url === '/favicon.ico') return;
console.log(`middleware one start => user:${ctx.user}`);
// 因为next是异步函数, 所以我们需要使用await 来等待他执行完成
await next();
console.log(`middleware one end => user:${ctx.user}`);
});
// 第二个中间件
app.use(async (ctx, next) => {
console.log(`middleware two start => user:${ctx.user}`);
await next();
console.log(`middleware tow end => user:${ctx.user}`);
});
// 第三个中间件
app.use(async (ctx, next) => {
// 我们在第三个中间件上面给ctx对象添加一个user属性
ctx.user = 'admin';
console.log(`middleware three start => user:${ctx.user}`);
await next();
console.log(`middleware three end => user:${ctx.user}`);
})
// 第四个中间件
.use(async (ctx, next) => {
console.log(`middleware four start => user:${ctx.user}`);
await next();
// 我们在第四个中间件去改变这个user属性的值
ctx.user = '小芳';
ctx.response.body = 'hello world';
console.log(`middleware four end => user:${ctx.user}`);
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
下图为上面中间件的执行顺序
自己写一个中间件, 来记录请求的方法, 请求的 url, 以及请求的 host
上面我们已经看到了koa中间件的流程, 那么我们接下来就写一个中间件, 用来记录每次请求的方法, url, 以及host, 和请求的时间
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
// 不解析 /favicon.ico 路由
if (ctx.url === '/favicon.ico') return;
const startTime = Date.now();
const {
request: { method, url, host, path }
} = ctx;
await next();
const endTime = Date.now();
ctx.response.body = `method: ${method}; url: ${url}; host: ${host}; path: ${path}; 本次请求的响应时间为: ${endTime -
startTime};`;
});
// 通过延迟请求时间, 来观察koa的中间件
app.use(async (ctx, next) => {
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 3000);
});
await next();
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
使用 koa-bodyparser 来解决 koa 的 其他请求参数
由于在koa里面, 没有封装其他请求的参数获取方法, 所以我们先使用node原生的方法, 来获取post请求所带的参数, 如下:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
const { request, response, req } = ctx;
const { method, query } = request;
if (method === 'GET') {
response.body = `请求的方法为: ${method}; 请求的参数为: ${JSON.stringify(query)}`;
} else if (method === 'POST') {
let params = await new Promise(resolve => {
let params = '';
// post请求的参数不是一次性就发送的, 因为post请求可以发送大量的数据, 所以在node里面采用流的形式来传递,
// 分多次传递, 然后我们将它拼接起来.
req.on('data', chunk => {
// chunk 是一个buffer对象, 通过 toString() 方法将他进行序列化
params += chunk.toString();
});
// 当post的数据传递完成之后, end事件将会被调用一次, 这个时候, 我们就能拿到所有的post请求的参数了
req.on('end', () => {
resolve(params);
});
});
response.body = `请求的方法为: ${method}; 请求的参数为: ${params}`;
}
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
从上面可以看出来, 使用node的原生方法来处理post请求, 会十分的复杂, 所以有高手为我们提供了一个koa-bodyparser的中间件, 用来处理get以外的其他请求方式, 下面我将使用koa-bodyparser中间件来写一个小例子:
const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
// 注册中间件
app.use(bodyparser());
app.use(async (ctx, next) => {
const { request, response } = ctx;
const { method, path, body } = request;
if (method === 'GET') {
if (path === '/') {
response.type = 'text/html';
response.body = `
<h1>home页</h1>
<form action="/login" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<button type="submit">提交</button>
</form>
`;
}
} else if (method === 'POST') {
if (path === '/login') {
// 通过koa-bodyparser中间件解析之后, 参数会被存放到request.body里面
const { username, password } = body;
response.type = 'text/plain';
response.body = `你的用户名是: ${username}; 你的密码是: ${password}`;
}
}
});
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
从上面可以看出, 使用koa-bodyparser来处理请求的参数, 会大大的减少我们代码量, 但是我们又发现, 我们在处理路由的时候, 也使用了很多的判断, 那么有没有一个中间件来帮我们解决路由的问题呢? 让我们一起接着往下看
使用 koa-router 来处理路由
const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
// 引入路由中间件
const Router = require('koa-router');
const router = new Router();
// 注册中间件
app.use(bodyparser());
router.get('/', async (ctx, next) => {
const { response } = ctx;
response.body = `
<h1>home页</h1>
<form action="/login" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<button type="submit">提交</button>
</form>
`;
});
router.post('/login', async (ctx, next) => {
const { request, response } = ctx;
const { body } = request;
const { username, password } = body;
response.body = `
<h1>欢迎登录</h1>
<div>你的账号是: ${username}</div>
<div>你的密码是: ${password}</div>
`;
});
app.use(router.routes());
app.use(router.allowedMethods()); // 路由的异常处理
app.listen(3000, () => {
console.log(`http://127.0.0.1:3000`);
});
从上面的例子, 我们可以轻松的写出和我们没有使用koa-router的时候, 一样的效果! 另外, 在koa-router里面,他会给我们提供了丰富的api, 让我们去使用, 下面我们就来尝试一下其他的api. 在一个接口内写多个中间件,我们来写一个参数验证的中间件, 如果参数正确, 就返回正确的页面, 否则就重定向到登录页面,将上面的代码做一点小的修改即可, 如下:
router.get('/', async (ctx, next) => {
const { response } = ctx;
response.body = `
<h1>请使用 admin 用户登录</h1>
<form action="/login" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<button type="submit">提交</button>
</form>
`;
});
const rule = async (ctx, next) => {
const { request } = ctx;
// 我们用来验证, 用户名是不是 admin, 如果不是,
// 我们就让他回到登录页, 重新登录
const {
body: { username }
} = request;
if (username === 'admin') {
await next();
} else {
// 重定向到登录页
ctx.redirect('/');
}
};
router.post('/login', rule, async (ctx, next) => {
const { request, response } = ctx;
const { body } = request;
const { username, password } = body;
response.body = `
<h1>欢迎登录</h1>
<div>你的账号是: ${username}</div>
<div>你的密码是: ${password}</div>
`;
});
通过上面的两个小例子, 我们大致可以知道了koa-router的基本用法, 当然他还有其他的一些属性和方法, 大家可以通过官方文档, 也欢迎大家在评论区留言讨论,第二章的内容, 就到此结束了, 我们下一章见