koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,经过组合不一样的 generator,能够免除重复繁琐的回调函数嵌套,并极大地提高错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得驾轻就熟。html
koa1.x的用generator, koa2.x用async/await, node各个版本对es6的支持程度,可参考 node.greennode
Koa 应用是一个包含一系列中间件 generator 函数的对象。 这些中间件函数基于 request 请求以一个相似于栈的结构组成并依次执行。 Koa 相似于其余中间件系统(好比 Ruby's Rack 、Connect 等), 然而 Koa 的核心设计思路是为中间件层提供高级语法糖封装,以加强其互用性和健壮性,并使得编写中间件变得至关有趣。git
Koa 包含了像 content-negotiation(内容协商)、cache freshness(缓存刷新)、proxy support(代理支持)和 redirection(重定向)等经常使用任务方法。 与提供庞大的函数支持不一样,Koa只包含很小的一部分,由于Koa并不绑定任何中间件。es6
hello world示例:github
//koa 2.x var Koa = require('koa'); var app = new Koa(); app.use(async (ctx, next) => { ctx.body = 'Hello World'; }); app.listen(3000); console.log('server listening at 3000');
Koa 的中间件经过一种更加传统(您也许会很熟悉)的方式进行级联,摒弃了以往 node 频繁的回调函数形成的复杂代码逻辑。 咱们经过 generators 来实现“真正”的中间件。 Connect 简单地将控制权交给一系列函数来处理,直到函数返回。 与之不一样,当执行到 yield next 语句时,Koa 暂停了该中间件,继续执行下一个符合请求的中间件('downstrem'),而后控制权再逐级返回给上层中间件('upstream')。web
koa1.x 和 koa2.x的中间件机制是同样的,区别在于koa1.x用generator书写,koa2.x用async/await; async/await本质上是generator的语法糖,generator的本质是一个迭代器,须要runner去触发下一阶段的执行.
中间件的栈执行顺序(后进先出),相似html标签层层嵌套,从顶层到底层,在从底层开始闭合(其实dom的事件机制也相似)json
示例:缓存
//> koa 2.x var Koa = require('koa'); var app = new Koa(); // mw: x-response-time app.use(async (ctx,next) => { let start = new Date; await next(); let ms = new Date - start; ctx.set('X-Response-time', ms + 'ms'); }); // mw: logger app.use(async (ctx, next) => { let start = new Date; await next(); let ms = new Date - start; console.log('%s %s - %d', ctx.method, ctx.url, ms); }); // mw: response app.use(async (ctx, next) => { ctx.body = 'Hello World'; // 设置响应内容后,还能够 ctx.se(..) 设置响应头 }); app.listen(3012); console.log('server listening at 3012'); //> koa 1.x var koa = require('koa'); var app = koa(); // x-response-time app.use(function* (next) { let start = new Date; yield next; var ms = new Date - start; this.set('X-Response-Time', ms + 'ms'); }); // logger app.use(function* (next) { let start = new Date; yield next; let ms = new Date - start; console.log('%s %s - %d', this.method, this.url, ms); }); // response app.use(function* (next) { this.body = 'Hello World~~'; }); app.listen(3012); console.log('server listen at 3012');
app实例的配置属性:服务器
app.listen(3000)
建立并返回一个http服务器.cookie
// app.listen() 同下 var http = require('http'); http.createServer(app.callback()).listen(3000); http.createServer(app.callback()).listen(3001); // 同一个应用运行在多个端口上
app.callback()
返回request callback函数,可用于 http.createServer(app.callback())
app.use(mw)
为应用添加中间件
app.keys = ['i am secrect', 'hello']
设置 signed cookie 的密钥
ctx.cookies.set('name', 'sandy', {signed: true})
设置加密的cookie
默认全部的错误信息会输出到 stderr, 能够监听error事件,实现自动的错误处理.
app.on('error', function(err, context) { // 若处理请求阶段出错,不能响应时,会有context参数传入回调 console.error('server error:', err); });
Koa Context 将 node 的 request 和 response 对象封装在一个单独的对象里面,其为编写 web 应用和 API 提供了不少有用的方法。
context 在每一个 request 请求中被建立.
koa 1.x 的上下文对象为中间件的 this, koa 2.x 的上下文对象,为中间件的参数1
// koa 1.x app.use(function* (next) { this.body = 'good'; // this is context obj }); // koa 2.x app.use(async (ctx, next) => { ctx.body = 'good2'; // ctx is context obj });
为了便于访问和调用,许多 context 的属性和方法,代理了 ctx.request 和 ctx.response 所对应的等价方法, 好比说 ctx.type 和 ctx.length 代理了 response 对象中对应的方法,ctx.path 和 ctx.method 代理了 request 对象中对应的方法。
ctx.req
nodejs的request对象ctx.res
nodejs的response对象ctx.request
koa的request对象, 包装后的nodejs的request对象ctx.response
koa的response对象ctx.app
应用实例的引用ctx.cookies.get(name, [options])
获取coolie, koa使用了Express的cookies模块ctx.cookies.set(name, value, [options])
设置cookie,
options = { signed: true, expires: time, path: '/', domain: 'a.com', secure: false, httpOnly: true }
ctx.throw(msg, [status])
抛出异常
// 适配 msg status两个参数的顺序,缺乏的则用默认值 ctx.throw(403); ctx.throw('some error'); ctx.throw('name required', 400); ctx.throw(400, 'name required');
ctx.response
ctx.response = false 时,能够经过ctx.res
直接使用原生的response对象, 但不推荐这样作
ctx.request
对象释放到 ctx
上的方法和属性
ctx.url
ctx.originalUrl
read onlyctx.protocol
read onlyctx.host
read onlyctx.hostname
read onlyctx.path
ctx.query
ctx.querystring
ctx.method
ctx.header
read onlyctx.get(headerName)
ctx.is()
判断 content-typectx.accepts()
accepts content-typectx.acceptsEncodings()
ctx.acceptsLanguage()
ctx.accepetsCharsets()
ctx.ip
read onlyctx.ips
read onlyctx.secure
read onlyctx.subdomains
read onlyctx.fresh
read onlyctx.stale
read onlyctx.socket
read onlyctx.response
对象释放到 ctx
上的方法和属性
ctx.body
ctx.attachment()
ctx.set(name,val)
设置响应头ctx.remove()
删除响应头ctx.status
状态码ctx.length
content-lengthctx.type
content-typectx.lastModified
缓存相关响应头ctx.etag
ctx.headerSent
是否已发送响应头ctx.redirect()
重定向ctx.request
对象的完整属性方法
ctx.request.method
ctx.request.url
ctx.request.originalUrl
ctx.request.protocol
ctx.request.host
hostname:portctx.request.hostname
ctx.request.path
ctx.request.query
eg: {color: 'blue', size: 'small'} 不支持嵌套对象ctx.request.querystring
eg: foo=bar&go=finectx.request.search
eg: ?foo=bar&go=finectx.request.get()
ctx.request.header
ctx.request.type
content-type eg: image/pngctx.request.length
cotent-lengthctx.request.charset
eg: utf-8ctx.request.is()
eg: ctx.is('html'), ctx.is('text/html'), ctx.is('html', 'application/*'), ctx.is('application/json')ctx.request.fresh
若用(*If-None-Match/ETag, If-Modified-Since/Last-Modified)进行缓存协商,则该属性表示协商结果ctx.request.stale
与 ctx.request.fresh 相反ctx.request.secure
ctx.request.ip
ctx.request.ips
ctx.request.subdomains
ctx.request.socket
ctx.request.idempotent
请求是否幂等ctx.request.accepts(types)
eg: ctx.accepts('html') ctx.accepets('json', 'text')ctx.request.acceptsEncoding(encodings)
eg: ctx.acceptsEncodings('gzip', 'deflate','identity')ctx.request.acceptsLanguages(langs)
eg: this.acceptsLanguages('es', 'en')ctx.request.acceptsCharsets(charsets)
eg: ctx.acceptsCharsets('utf-8')ctx.response
对象的完整属性
ctx.response.body
eg: ctx.body = String/Buffer/Stream/Objectctx.response.attachment([filename])
ctx.response.get(field)
eg: ctx.response.get('ETag') 响应头(field)不区分大小写ctx.response.set(field, value)
eg: ctx.set('cache-control', 'no-cache')ctx.response.remove(field)
ctx.response.append(field, val)
为指定头部追加值ctx.response.header
ctx.response.status
eg: ctx.response.status = 301;ctx.response.statusString
ctx.response.length
content-lengthctx.response.type
content-type eg: image/png , .png , pngctx.response.lastModified
eg: ctx.lastModified = new Date()ctx.response.etag
eg: ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');ctx.response.headerSent
ctx.response.vary(field)
等价于 ctx.response.append('Vary', field)ctx.response.socket
ctx.response.redirect(url, [alt])
ctx.redirect('back'); ctx.redirect('back', '/index.html') ctx.redirect('/login'); ctx.redirect('http://google.com');