koa2框架的使用与解析

koa是什么?

koa是基于nodejs的一个http中间件框架。koa源码只有一千多行,所有的功能都可以通过插件实现,简单易懂,自由度高。

准备

  1. node需要v7.6.0或者更高的版本(支持es6和async的语法)。
  2. npm install koa

开始

先看看来自官方github的最小demo

const Koa = require('koa');
const app = new Koa();

// response
app.use((ctx,next) => {
  ctx.body = 'Hello Koa';
});

app.listen(3000);
复制代码

不难发现,相对于原生nodejs来说,只多了uselisten两个方法和ctxnext两个参数。

中间件

koa的一个最重要的设计就是中间件。

1. 中间件是什么?

中间件(middleware)本质上就是一个函数,处于http request和http response中间,实现某种中间的功能。使用app.use()来加载中间件。koa中每个中间件默认两个参数,第一个是ctx对象,第二个是next函数。我们可以使用next函数来把执行权转交给下一个中间件。

2. 中间件栈

多个中间件会形成一个栈结构,以"先进后出"的原则执行。

const one = (ctx, next) => {
  console.log('1');
  next();
  console.log('2');
}

const two = (ctx, next) => {
  console.log('3');
  next(); 
  console.log('4');
}

const three = (ctx, next) => {
  console.log('5');
  next();
  console.log('6');
}

app.use(one);
app.use(two);
app.use(three);

// 1 3 5 6 4 2
复制代码
3. 移除next?

中间件通过next来将函数执行权移交到下一个中间件,若无next,执行权就不会移交下一个中间件,后续中间件无效。

app.use((crx, next) => {
  console.log(1)
  next()
  console.log(2)
})
app.use((crx, next) => {
  console.log(3)
  // next()
  console.log(4)
})
app.use((crx, next) => {
  console.log(5)
  next()
  console.log(6)
})
app.listen(3000);
// 1 3 4 2
复制代码

use

  1. 每一次use传入一个回调函数f,listen时执行这个函数f,demo的回调函数f是
(ctx) => {
  ctx.body = 'Hello Koa';
}
复制代码
  1. 回调函数f一共有两个参数,分别是context(以下简写ctx)和next。第一个参数ctx是原生req、res经过一系列处理后产生的对象。 函数f的第二个参数next()跳到下一个回调函数,多个use的回调函数按照顺序执行。(例子见中间件-中间件栈)

listen

listen的实现原理其实就是对http.createServer进行了一个封装,重点是这个函数中传入的callback。callback就是所有中间件的组合。

原生node监听3000端口

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write('Hello Http Server');
    res.end();
}).listen(3000);
复制代码

koa的封装实现

listen(...args) {
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }
复制代码

ctx

koa中, ctx表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。为了开发方便而设计的一个js对象,绑定了请求和响应相关的数据和方法(如ctx.path、ctx.body等)。本质上使用request、response两个文件拓展属性,ctx通过delegate实现代理拿到request和response的方法和属性。

delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable');
  
delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  .access('query')
  .access('path')
  .access('url')
  .access('accept')
  .getter('origin')
  .getter('href')
  .getter('subdomains')
  .getter('protocol')
  .getter('host')
  .getter('hostname')
  .getter('URL')
  .getter('header')
  .getter('headers')
  .getter('secure')
  .getter('stale')
  .getter('fresh')
  .getter('ips')
  .getter('ip');
复制代码

比如我们要访问ctx.repsponse.status,在开发中可以直接访问ctx.status。

next

在中间件中,使用next函数来把执行权转交给下一个中间件。

路由

网站一般有多个页面/接口,通过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';
  }
}
复制代码

随着项目不断迭代,页面/接口数量会上升。上述代码不易维护,此时可以使用koa-router

错误捕获

在发生错误的时候,能够捕获到错误和抛出的异常,并反馈出来。koa中有以下两种比较通用的捕获错误的方法。

  1. 运行出错时,koa会触发一个error事件,我们可以监听这个事件来处理错误。
app.on('error', (err,ctx) => {
    console.error('server error', err)
});
复制代码
  1. 自己写一个最外层的中间件,负责所有中间件的错误处理。
const errorHandler = async (ctx,next) => {
    try {
        await next();
    } catch (err) {
        ctx.status = err.statusCode || err.status || 500;
        ctx.body = {
            message: err.message
        }
    }
}
复制代码

当错误被try...catch捕获时,就不会触发koa的error事件。当我们catch捕获到错误时,可以手动触发error事件。

const errorHandler = async (ctx,next) => {
    try {
        await next();
    } catch (err) {
        ctx.status = err.statusCode || err.status || 500;
        ctx.body = {
            message: err.message
        }
        ctx.app.emit('error', err, ctx) // 手动触发error事件
    }
}
复制代码

参考

转载于:https://juejin.im/post/5cac5731f265da03775c3058

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值