盒子
盒子
文章目录
  1. koa简介
    1. koa的代码级联
    2. Koa配置
    3. app.listen( … )监听端口
    4. app.callback()
    5. app.use(middleWare)
    6. app.context
    7. 错误处理
  2. Context
    1. Request
    2. Response

koa简介

koa简介

初试koa,看了下官网介绍,记录一下学习心得。

koa是致力于成为一个更小、更健壮、更富有表现力的 Web 框架。跟express相比,最核心的部分就是用新的async function 解决了回调函数的噩梦。

比如用express实现一个计算程序处理时间的功能,用express写法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
var express = require('express');
var app = express();
app.use( '/' , function( req , res , next ){
req.startTime = new Date() ;
next() ;
} ) ;
app.use( '/' , function( req , res , next ){
req.endTime = new Date() ;
res.end( 'time diff: ' + ( req.startTime - req.endTime ) + 'ms' ) ;
} ) ;

express的中间件形式,需要每个中间件手动调用next函数,把执行权限移交到下一个注册的中间件。

如果用koa来写,代码如下:

1
2
3
4
5
6
7
8
9
const Koa = require( 'koa' ) ;
const app = new Koa();
app.use(async function (ctx, next) {
const startTime = new Date();
await next();
const timeDiff = new Date() - startTime;
ctx.body = `time diff: ${timeDiff}ms`;
});
app.listen(3001);

相对而言,代码简单了很多。想要学习koa,先要学习一下基础知识点,async函数。

koa的代码级联

代码级联是通过async函数实现的(Koa v2.x 移除了内置的 Generator 支持,使用 async 函数替代),先简单介绍下async函数,语法很简单:

1
2
3
4
async function asyncFunc(){
// 函数体
var data = await fetchSomeThing() ;
}

async函数返回一个promise对象,所以可以使用then方法添加回调函数。当函数体执行的时候,遇到await的时候,会执行后面的表达式,表达式可以是原始值类型或者返回Promise对象。如果是个异步表达式的话,javascript会中断本次执行,执行其他内容。等异步执行结束,会返回到上次中断的地方,继续往下执行。

比如,我想中断两秒钟,返回一个hello world!。代码如下:

1
2
3
4
5
6
7
async function delayTwoSeconds(){
var result = await new Promise( ( resolve , reject )=>{
setTimeout( resolve.bind(null,'hello world!') , 2000 ) ;
} ) ;
return result
}
delayTwoSeconds().then( data=>console.log( data ) ) ; // 2m后,控制台:hello world!

当执行的时候,result并不会立即返回,而是等待2秒,然后返回。

用async函数有什么好处?

  • 代码结构清晰,执行过程明了,避免了回调嵌套的代码
  • 函数的作用域得以保存,不再用callback( data ) 函数传参
  • 易于错误的捕获

第三点,怎么错误捕获?平常如果异步回调函数执行的时候,一般都是在callback中,捕获错误 , 因为当执行异步操作的时候,异步函数代码执行完毕,javascript执行其他内容的时候,函数的资源会被垃圾回收,等事件机制通知回调函数的时候,原来的资源早已不在,所以如果发生错误,也是只能通过函数传参。如下:

1
2
3
var callback = function( error , data ){
if( error ) { throw error ; }
}

而使用async函数,因为函数只是暂停执行,里面的变量并不会被回收,所以可以继续使用。

koa利用了async的特性,让嵌套中间件执行之后,不用通过绑定全局变量,或者在request对象上绑定属性来传参了,直接在函数体里面用之前定义的变量,方便了很多。koa的async调用机制的介绍完毕,下面是一些常用api的说明。

Koa配置

koa的应用程序配置默认在app实例上,目前支持

  • app.env 默认为NODE_ENV,如果没有配置的话,为"development"
  • app.proxy 当设置为true时,支持 X-Forwarded-Host
  • app.subdomainOffset 子域名偏移量,属性默认为2,如域名“tobi.ferrets.example.com” 执行:ctx.subdomains 返回[“ferrets”, “tobi”],如果app.subdomainOffset设为3,则返回[“tobi”]。

app.listen( … )监听端口

1
2
3
4
5
6
7
8
const Koa = require('koa');
const app = new Koa();
app.listen(3000); // 启动3000端口
// 等价于
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);

还是使用了node内置的http模块

app.callback()

执行这个函数返回的函数可以用于处理http.createServer()的请求,也可以用这个回调函数将你的koa应用挂载在express或者Connect应用中。

app.use(middleWare)

koa中这个方法很常用了,将给定的middleWare当作中间件加载到应用中。middleWare接受两个参数ctxnext,第一个参数是上下文,第二个参数是执行下一个中间件的回调。

app.context

app.context是middleWare第一个参数ctx实例的原型。想要给ctx添加属性,可以直接编辑app.context。对于在应用程序中经常使用的属性或者函数,这个api非常有用。不过不建议过度依赖,毕竟类似全局变量,而全局变量是evil的😈。官网给的例子,比如数据库实例可以挂在到ctx上。

1
2
3
4
app.context.db = db();
app.use(async (ctx) => {
console.log(ctx.db);
});

错误处理

默认所有的错误都通过stderr输出,如果设置了app.silent为true的话,就不输出。当err.status是404或者err.expose等于true,则默认不输出。如果想自定义错误处理逻辑, 可以定义一个「错误事件」来监听 Koa app 中发生的错误:

1
2
3
app.on('error', err =>
log.error('server error', err)
);

当 req/res 周期中出现任何错误且无法响应客户端时,Koa 会把 Context(上下文) 实例作为第二个参数传递给 error 事件。当错误发生时,如果还没有数据返回客户端,koa会响应500 内部错误给客户端,并且会触发app-level的error事件,可以用来记录日志。

Context

一个koa的Context封装了node原生的request和response对象,提供了一些方便的API供应用程序开发。

随着每个request请求,都会创建一个新的Context,当中间件被调用时,它会被会传到给每个中间件的第一个参数中。

1
2
3
4
5
app.use(async (ctx, next) => {
ctx; // is the Context
ctx.request; // is a koa Request
ctx.response; // is a koa Response
});

ctx中的很多访问器和方法,简单代理了ctx.request或者ctx.resposne ,因为有些高频API,直接代理了方便使用。比如ctx.typectx.length代理了response对象,ctx.pathctx.method代理了request对象。

  • ctx.req node的request对象

  • ctx.res node的resposne对象

    避开koa,而直接操作node底层的response对象要避免。比如

    • res.statusCode
    • res.writeHead()
    • res.write()
    • res.end()
  • ctx.request koa的request对象

  • cox.response koa的response对象

  • ctx.state koa推荐挂载数据的地方,比如用户信息

    1
    ctx.state.user = await User.find(id) ;
  • ctx.app 应用程序实例

  • cox.throw 自定义状态码响应的方法。比如

    1
    2
    3
    4
    ctx.throw(403);
    ctx.throw('name required', 400);
    ctx.throw(400, 'name required');
    ctx.throw('something exploded');
  • cox.respond 如不想使用koa内置的response处理方法,可以设置this.respond=false,跳过koa针对response的预处理,你可以自己设置response对象。算是一种hack方法吧,一般不常用

    ……

Request

简单介绍一下koa的request对象,一些常用的API

  • request.header 请求头
  • request.method 请求方法
  • request.length 请求头的Context-Length
  • request.originalUrl 获取request的originalUrl
  • request.origin origin URL,包括,protocol和host
  • request.path 请求路径
  • request.querystring 查询参数 , 不包括?
  • request.search 原始的query string,包括?
  • request.query 结构化了的查询参数

….

Response

简单介绍一下koa的response对象,一些常用的API

  • response.header 响应对象
  • response.status 响应状态,默认无值
  • response.message 响应消息,一般和状态码一起设置
  • response.length 设置响应的大小
  • response.body 返回响应内容
  • response.get(field) 获取响应的头部字段
  • response.set(field,value) 设置响应头的内容
  • response.append(field,value) 追加额外的头部字段内容
  • response.redirect( url , [alt] ) 重定向,可以自定’back’,能提供Referrer支持,没有时,可用alt指定
  • response.lastModified 如果响应头部包含Last-Modified,则用Date对象返回

…..

具体API可以查看本文后面的官网链接。

最后介绍一个debug模块的使用

通常我们开发环境需要使用console来打印调试一些内容,但是生产环境又不需要这些内容,可以使用debug模块,debug模块可以对log进行分组,而且只有设置了DEBUG的情况下,才会输出内容。

1
npm install debug

接下来在代码中引入

1
2
3
4
5
6
var debug = require('debug')('app'); // 这里自定现实哪一块DEBUG的内容
app.use(async function ( ctx, next ){
var config = ctx.app ;
debug('env: %s',config.env) ;
ctx.body = config.env;
});

接下来就可以启动了:

1
DEBUG=app node index.js

参考文档:

Koa next generation web framework for node.js

ECMAScript 6 入门

支持一下
扫一扫,支持lcoder