概述
Koa 是一个新的 web 框架, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa快速而愉快地编写服务端应用程序。
基本用法
1、架构一个HTTP服务
// demos/01.js
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
只要三行代码就可以使用Koa架构一个HTTP服务,那么我们来看看使用HTTP模块吧
const http = require('http')
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
}).listen(3000);
然而我们此时打开 http://127.0.0.1:3000,还是没有东西
2、Context对象
Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(main);
app.listen(3000);
打开浏览器,可以看到对应的页面显示
3、Http Response的类型
Koa默认的返回类型是text/plain,如果想返回其他类型的内容,那我需要先用ctx.request.accepts判断一下(即对应HTTP请求里面的Accept字段),然后使用ctx.response.type指定返回类型。
4、路由
原生路由
我们可以通过ctx.request.path来获取用户请求的路径。
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page</a>';
} else {
ctx.response.body = 'Hello World';
}
};
但是会有一个问题,就是当你的路由路径很多的使用,你就不得不使用很多个if-else了,或者使用switch-case,这样会有一些不是很美观。
Koa为我们提供了koa-route模块
我们需要新安装对应的koa-route模块
const route = require('koa-route');
const about = ctx => {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page</a>';
};
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(route.get('/', main));
app.use(route.get('/about', about));
这样我们只需要在app.use进行引入即可
5、静态资源
koa-static模块封装了静态资源的请求。
//index.js
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
这样我们就可以访问这个serve里面对应的路径里面的资源了
比如我们可以访问http://127.0.0.1:3000/index.js,然后就可以访问我们这个脚本文件了。
6、重定向
有些场合,服务器需要重定向,比如用户登陆后,重定向到登陆之前的页面。
ctx.response.redirect()可以发出一个302跳转,将用户导向另一个路由
const redirect = ctx => {
ctx.response.redirect('/');
ctx.response.body = '<a href="/">Index Page</a>';
};
app.use(route.get('/redirect', redirect));
比如你访问http://localhost:3000/redirect,浏览器会将用户重定向到根路由。
7、中间件
处于在HTTP request和HTTP response中间,用来实现某些功能。app.use可以用来加载中间件。
中间件的本质就是一个函数,第一个参数是Context对象,第二个参数是next函数。只要调用next函数,就会把执行权转交给下一个中间件。
8、中间件栈
多个中间件会形成一个栈结构,以“先进后出”的顺序执行。
-
最外层的中间件首先执行。
-
调用
next
函数,把执行权交给下一个中间件。 -
...
-
最内层的中间件最后执行。
-
执行结束后,把执行权交回上一层的中间件。
-
...
-
最外层的中间件收回执行权之后,执行
next
函数后面的代码。
比如下面这个例子:
const one = (ctx, next) => {
console.log('>> one');
next();
console.log('<< one');
}
const two = (ctx, next) => {
console.log('>> two');
next();
console.log('<< two');
}
const three = (ctx, next) => {
console.log('>> three');
next();
console.log('<< three');
}
app.use(one);
app.use(two);
app.use(three);
访问对应的url后,命令行输出的是
>> one
>> two
>> three
<< three
<< two
<< one
如果中间件内部没有调用next函数,那么执行权就不会传递下去。
9、异步中间件
如果有异步操作(比如读取数据库),中间件就必须写成 async函数。
const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa();
const main = async function (ctx, next) {
ctx.response.type = 'html';
ctx.response.body = await fs.readFile('./demos/template.html', 'utf8');
};
app.use(main);
app.listen(3000);
由于fs.readFile
是一个异步操作,必须写成await fs.readFile()
,然后中间件必须写成 async 函数。
10、错误处理
可以使用ctx.throw()方法,用来抛出错误,ctx.throw(500)、ctx.throw(404),我们也可以使用try...catch来捕获中间件的错误,我们可以让最外层的中间件处理所有中间件的错误。
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.body = {
message: err.message
};
}
};
const main = ctx => {
ctx.throw(500);
};
app.use(handler);
app.use(main);
我们可以使用error事件的监听
const main = ctx => {
ctx.throw(500);
};
app.on('error', (err, ctx) =>
console.error('server error', err);
);
如果错误被try...catch
捕获,就不会触发error
事件。这时,必须调用ctx.app.emit()
,手动释放error
事件,才能让监听函数生效。
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.type = 'html';
ctx.response.body = '<p>Something wrong, please contact administrator.</p>';
ctx.app.emit('error', err, ctx);
}
};
const main = ctx => {
ctx.throw(500);
};
app.on('error', function(err) {
console.log('logging error ', err.message);
console.log(err);
});
11、cookies
ctx.cookies可以读写Cookie
12、表单
koa-body可以用来从POST请求的数据体里面提取键值对。