Koa基本使用

483 阅读3分钟

概述

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、中间件栈

多个中间件会形成一个栈结构,以“先进后出”的顺序执行。

  1. 最外层的中间件首先执行。

  2. 调用next函数,把执行权交给下一个中间件。

  3. ...

  4. 最内层的中间件最后执行。

  5. 执行结束后,把执行权交回上一层的中间件。

  6. ...

  7. 最外层的中间件收回执行权之后,执行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请求的数据体里面提取键值对。