简介
koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 koa 编写 web 应用,经过组合不一样的 generator,能够免除重复繁琐的回调函数嵌套, 并极大地提高错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得驾轻就熟。javascript
koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 koa 编写 web 应用,经过组合不一样的 generator,能够免除重复繁琐的回调函数嵌套, 并极大地提高错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得驾轻就熟。javascript
Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.css
你可使用本身喜欢的版本管理器快速安装支持的 node 版本:html
$ nvm install 7 $ npm i koa $ node my-koa-app.js
要在 node < 7.6 版本的 Koa 中使用 async
方法, 咱们推荐使用 babel's require hook.前端
require('babel-register'); // 应用的其他 require 须要被放到 hook 后面 const app = require('./app');
要解析和编译 async 方法, 你至少应该有 transform-async-to-generator 或 transform-async-to-module-method 插件.java
例如, 在你的 .babelrc
文件中, 你应该有:node
{
"plugins": ["transform-async-to-generator"] }
你也能够用 env preset 的 target 参数 "node": "current"
替代.git
Koa 应用程序是一个包含一组中间件函数的对象,它是按照相似堆栈的方式组织和执行的。 Koa 相似于你可能遇到过的许多其余中间件系统,例如 Ruby 的 Rack ,Connect 等,然而,一个关键的设计点是在其低级中间件层中提供高级“语法糖”。 这提升了互操做性,稳健性,并使书写中间件更加愉快。github
这包括诸如内容协商,缓存清理,代理支持和重定向等常见任务的方法。 尽管提供了至关多的有用的方法 Koa 仍保持了一个很小的体积,由于没有捆绑中间件。web
必修的 hello world 应用:数据库
const Koa = require('koa'); const app = new Koa(); app.use(async ctx => { ctx.body = 'Hello World'; }); app.listen(3000);
Koa 中间件以更传统的方式级联,您可能习惯使用相似的工具 - 以前难以让用户友好地使用 node 的回调。然而,使用 async 功能,咱们能够实现 “真实” 的中间件。对比 Connect 的实现,经过一系列功能直接传递控制,直到一个返回,Koa 调用“下游”,而后控制流回“上游”。
下面以 “Hello World” 的响应做为示例,首先请求流经过 x-response-time
和 logging
中间件来请求什么时候开始,而后继续移交控制给 response
中间件。当一个中间件调用 next()
则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开而且每一个中间件恢复执行其上游行为。
const Koa = require('koa'); const app = new Koa(); // x-response-time app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`); }); // logger app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}`); }); // response app.use(async ctx => { ctx.body = 'Hello World'; }); app.listen(3000);
应用程序设置是 app
实例上的属性,目前支持以下:
app.env
默认是 NODE_ENV 或 "development"app.proxy
当真正的代理头字段将被信任时app.subdomainOffset
对于要忽略的 .subdomains
偏移[2]Koa 应用程序不是 HTTP 服务器的1对1展示。 能够将一个或多个 Koa 应用程序安装在一块儿以造成具备单个HTTP服务器的更大应用程序。
建立并返回 HTTP 服务器,将给定的参数传递给 Server#listen()
。这些内容都记录在 nodejs.org.
如下是一个无做用的 Koa 应用程序被绑定到 3000
端口:
const Koa = require('koa'); const app = new Koa(); app.listen(3000);
这里的 app.listen(...)
方法只是如下方法的语法糖:
const http = require('http'); const Koa = require('koa'); const app = new Koa(); http.createServer(app.callback()).listen(3000);
这意味着您能够将同一个应用程序同时做为 HTTP 和 HTTPS 或多个地址:
const http = require('http'); const https = require('https'); const Koa = require('koa'); const app = new Koa(); http.createServer(app.callback()).listen(3000); https.createServer(app.callback()).listen(3001);
返回适用于 http.createServer()
方法的回调函数来处理请求。你也可使用此回调函数将 koa 应用程序挂载到 Connect/Express 应用程序中。
将给定的中间件方法添加到此应用程序。参阅 Middleware 获取更多信息.
设置签名的 Cookie 密钥。
这些被传递给 KeyGrip,可是你也能够传递你本身的 KeyGrip
实例。
例如,如下是能够接受的:
app.keys = ['im a newer secret', 'i like turtle']; app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
这些密钥能够倒换,并在使用 { signed: true }
参数签名 Cookie 时使用。
ctx.cookies.set('name', 'tobi', { signed: true });
app.context
是从其建立 ctx
的原型。您能够经过编辑 app.context
为 ctx
添加其余属性。这对于将 ctx
添加到整个应用程序中使用的属性或方法很是有用,这可能会更加有效(不须要中间件)和/或 更简单(更少的 require()
),而更多地依赖于ctx
,这能够被认为是一种反模式。
例如,要从 ctx
添加对数据库的引用:
app.context.db = db();
app.use(async ctx => { console.log(ctx.db); });
注意:
ctx
上的许多属性都是使用 getter
,setter
和 Object.defineProperty()
定义的。你只能经过在 app.context
上使用 Object.defineProperty()
来编辑这些属性(不推荐)。查阅 https://github.com/koajs/koa/issues/652.ctx
和设置。 所以,安装的应用程序只是一组中间件。默认状况下,将全部错误输出到 stderr,除非 app.silent
为 true
。 当 err.status
是 404
或 err.expose
是 true
时默认错误处理程序也不会输出错误。 要执行自定义错误处理逻辑,如集中式日志记录,您能够添加一个 “error” 事件侦听器:
app.on('error', err => { log.error('server error', err) });
若是 req/res 期间出现错误,而且 _没法_ 响应客户端,Context
实例仍然被传递:
app.on('error', (err, ctx) => { log.error('server error', err, ctx) });
当发生错误 _而且_ 仍然能够响应客户端时,也没有数据被写入 socket 中,Koa 将用一个 500 “内部服务器错误” 进行适当的响应。在任一状况下,为了记录目的,都会发出应用级 “错误”。
Koa Context 将 node 的 request
和 response
对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操做在 HTTP 服务器开发中频繁使用,它们被添加到此级别而不是更高级别的框架,这将强制中间件从新实现此通用功能。
_每一个_ 请求都将建立一个 Context
,并在中间件中做为接收器引用,或者 ctx
标识符,如如下代码片断所示:
app.use(async ctx => { ctx; // 这是 Context ctx.request; // 这是 koa Request ctx.response; // 这是 koa Response });
为方便起见许多上下文的访问器和方法直接委托给它们的 ctx.request
或 ctx.response
,否则的话它们是相同的。 例如 ctx.type
和 ctx.length
委托给 response
对象,ctx.path
和 ctx.method
委托给 request
。
Context
具体方法和访问器.
Node 的 request
对象.
Node 的 response
对象.
绕过 Koa 的 response 处理是 不被支持的. 应避免使用如下 node 属性:
res.statusCode
res.writeHead()
res.write()
res.end()
koa 的 Request
对象.
koa 的 Response
对象.
推荐的命名空间,用于经过中间件传递信息和你的前端视图。
ctx.state.user = await User.find(id);
应用程序实例引用
经过 options
获取 cookie name
:
signed
所请求的cookie应该被签名koa 使用 cookies 模块,其中只需传递参数。
经过 options
设置 cookie name
的 value
:
maxAge
一个数字表示从 Date.now() 获得的毫秒数signed
cookie 签名值expires
cookie 过时的 Date
path
cookie 路径, 默认是'/'
domain
cookie 域名secure
安全 cookiehttpOnly
服务器可访问 cookie, 默认是 trueoverwrite
一个布尔值,表示是否覆盖之前设置的同名的 cookie (默认是 false). 若是是 true, 在同一个请求中设置相同名称的全部 Cookie(无论路径或域)是否在设置此Cookie 时从 Set-Cookie 标头中过滤掉。koa 使用传递简单参数的 cookies 模块。
Helper 方法抛出一个 .status
属性默认为 500
的错误,这将容许 Koa 作出适当地响应。
容许如下组合:
ctx.throw(400); ctx.throw(400, 'name required'); ctx.throw(400, 'name required', { user: user });
例如 ctx.throw(400, 'name required')
等效于:
const err = new Error('name required'); err.status = 400; err.expose = true; throw err;
请注意,这些是用户级错误,并用 err.expose
标记,这意味着消息适用于客户端响应,这一般不是错误消息的内容,由于您不想泄漏故障详细信息。
你能够根据须要将 properties
对象传递到错误中,对于装载上传给请求者的机器友好的错误是有用的。这用于修饰其人机友好型错误并向上游的请求者报告很是有用。
ctx.throw(401, 'access_denied', { user: user });
koa 使用 http-errors 来建立错误。
当 !value
时,Helper 方法抛出相似于 .throw()
的错误。这与 node 的 assert() 方法相似.
ctx.assert(ctx.state.user, 401, 'User not found. Please login!');
koa 使用 http-assert 做为断言。
为了绕过 Koa 的内置 response 处理,你能够显式设置 ctx.respond = false;
。 若是您想要写入原始的 res
对象而不是让 Koa 处理你的 response,请使用此参数。
请注意,Koa _不_ 支持使用此功能。这可能会破坏 Koa 中间件和 Koa 自己的预期功能。使用这个属性被认为是一个 hack,只是便于那些但愿在 Koa 中使用传统的 fn(req, res)
功能和中间件的人。
如下访问器和 Request 别名等效:
ctx.header
ctx.headers
ctx.method
ctx.method=
ctx.url
ctx.url=
ctx.originalUrl
ctx.origin
ctx.href
ctx.path
ctx.path=
ctx.query
ctx.query=
ctx.querystring
ctx.querystring=
ctx.host
ctx.hostname
ctx.fresh
ctx.stale
ctx.socket
ctx.protocol
ctx.secure
ctx.ip
ctx.ips
ctx.subdomains
ctx.is()
ctx.accepts()
ctx.acceptsEncodings()
ctx.acceptsCharsets()
ctx.acceptsLanguages()
ctx.get()
如下访问器和 Response 别名等效:
ctx.body
ctx.body=
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type=
ctx.type
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=
Koa Request
对象是在 node 的 vanilla 请求对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。
请求标头对象。
设置请求标头对象。
请求标头对象。别名为 request.header
.
设置请求标头对象。别名为 request.header=
.
请求方法。
设置请求方法,对于实现诸如 methodOverride()
的中间件是有用的。
返回以数字返回请求的 Content-Length,或 undefined
。
获取请求 URL.
设置请求 URL, 对 url 重写有用。
获取请求原始URL。
获取URL的来源,包括 protocol
和 host
。
ctx.request.origin
// => http://example.com
获取完整的请求URL,包括 protocol
,host
和 url
。
ctx.request.href;
// => http://example.com/foo/bar?q=1
获取请求路径名。
设置请求路径名,并在存在时保留查询字符串。
根据 ?
获取原始查询字符串.
设置原始查询字符串。
使用 ?
获取原始查询字符串。
设置原始查询字符串。
获取当前主机(hostname:port)。当 app.proxy
是 true 时支持 X-Forwarded-Host
,不然使用 Host
。
存在时获取主机名。当 app.proxy
是 true 时支持 X-Forwarded-Host
,不然使用 Host
。
若是主机是 IPv6, Koa 解析到 WHATWG URL API, 注意 这可能会影响性能。
获取 WHATWG 解析的 URL 对象。
获取请求 Content-Type
不含参数 "charset"。
const ct = ctx.request.type; // => "image/png"
在存在时获取请求字符集,或者 undefined
:
ctx.request.charset;
// => "utf-8"
获取解析的查询字符串, 当没有查询字符串时,返回一个空对象。请注意,此 getter _不_ 支持嵌套解析。
例如 "color=blue&size=small":
{
color: 'blue', size: 'small' }
将查询字符串设置为给定对象。 请注意,此 setter _不_ 支持嵌套对象。
ctx.query = { next: '/login' };
检查请求缓存是否“新鲜”,也就是内容没有改变。此方法用于 If-None-Match
/ ETag
, 和 If-Modified-Since
和 Last-Modified
之间的缓存协商。 在设置一个或多个这些响应头后应该引用它。
// 新鲜度检查须要状态20x或304 ctx.status = 200; ctx.set('ETag', '123'); // 缓存是好的 if (ctx.fresh) { ctx.status = 304; return; } // 缓存是陈旧的 // 获取新数据 ctx.body = await db.find('something');
相反与 request.fresh
.
返回请求协议,“https” 或 “http”。当 app.proxy
是 true 时支持 X-Forwarded-Proto
。
经过 ctx.protocol == "https"
来检查请求是否经过 TLS 发出。
请求远程地址。 当 app.proxy
是 true 时支持 X-Forwarded-Proto
。
当 X-Forwarded-For
存在而且 app.proxy
被启用时,这些 ips 的数组被返回,从上游 - >下游排序。 禁用时返回一个空数组。
将子域返回为数组。
子域是应用程序主域以前主机的点分隔部分。默认状况下,应用程序的域名假定为主机的最后两个部分。这能够经过设置 app.subdomainOffset
来更改。
例如,若是域名为“tobi.ferrets.example.com”:
若是 app.subdomainOffset
未设置, ctx.subdomains
是 ["ferrets", "tobi"]
. 若是 app.subdomainOffset
是 3, ctx.subdomains
是 ["tobi"]
.
检查传入请求是否包含 Content-Type
头字段, 而且包含任意的 mime type
。 若是没有请求主体,返回 null
。 若是没有内容类型,或者匹配失败,则返回 false
。 反之则返回匹配的 content-type。
// 使用 Content-Type: text/html; charset=utf-8 ctx.is('html'); // => 'html' ctx.is('text/html'); // => 'text/html' ctx.is('text/*', 'text/html'); // => 'text/html' // 当 Content-Type 是 application/json 时 ctx.is('json', 'urlencoded'); // => 'json' ctx.is('application/json'); // => 'application/json' ctx.is('html', 'application/*'); // => 'application/json' ctx.is('html'); // => false
例如,若是要确保仅将图像发送到给定路由:
if (ctx.is('image/*')) { // 处理 } else { ctx.throw(415, 'images only!'); }
Koa的 request
对象包括由 accepts 和 negotiator 提供的有用的内容协商实体。
这些实用程序是:
request.accepts(types)
request.acceptsEncodings(types)
request.acceptsCharsets(charsets)
request.acceptsLanguages(langs)
若是没有提供类型,则返回 全部 可接受的类型。
若是提供多种类型,将返回最佳匹配。 若是没有找到匹配项,则返回一个false
,你应该向客户端发送一个406 "Not Acceptable"
响应。
若是接收到任何类型的接收头,则会返回第一个类型。 所以,你提供的类型的顺序很重要。
检查给定的 type(s)
是否能够接受,若是 true
,返回最佳匹配,不然为 false
。 type
值多是一个或多个 mime 类型的字符串,如 application/json
,扩展名称如 json
,或数组 ["json", "html", "text/plain"]
。
// Accept: text/html ctx.accepts('html'); // => "html" // Accept: text/*, application/json ctx.accepts('html'); // => "html" ctx.accepts('text/html'); // => "text/html" ctx.accepts('json', 'text'); // => "json" ctx.accepts('application/json'); // => "application/json" // Accept: text/*, application/json ctx.accepts('image/png'); ctx.accepts('png'); // => false // Accept: text/*;q=.5, application/json ctx.accepts(['html', 'json']); ctx.accepts('html', 'json'); // => "json" // No Accept header ctx.accepts('html', 'json'); // => "html" ctx.accepts('json', 'html'); // => "json"
你能够根据须要屡次调用 ctx.accepts()
,或使用 switch:
switch (ctx.accepts('json', 'html', 'text')) { case 'json': break; case 'html': break; case 'text': break; default: ctx.throw(406, 'json, html, or text only'); }
检查 encodings
是否能够接受,返回最佳匹配为 true
,不然为 false
。 请注意,您应该将identity
做为编码之一!
// Accept-Encoding: gzip ctx.acceptsEncodings('gzip', 'deflate', 'identity'); // => "gzip" ctx.acceptsEncodings(['gzip', 'deflate', 'identity']); // => "gzip"
当没有给出参数时,全部接受的编码将做为数组返回:
// Accept-Encoding: gzip, deflate ctx.acceptsEncodings(); // => ["gzip", "deflate", "identity"]
请注意,若是客户端显式地发送 identity;q=0
,那么 identity
编码(这意味着没有编码)多是不可接受的。 虽然这是一个边缘的状况,你仍然应该处理这种方法返回 false
的状况。
检查 charsets
是否能够接受,在 true
时返回最佳匹配,不然为 false
。
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 ctx.acceptsCharsets('utf-8', 'utf-7'); // => "utf-8" ctx.acceptsCharsets(['utf-7', 'utf-8']); // => "utf-8"
当没有参数被赋予全部被接受的字符集将做为数组返回:
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 ctx.acceptsCharsets(); // => ["utf-8", "utf-7", "iso-8859-1"]
检查 langs
是否能够接受,若是为 true
,返回最佳匹配,不然为 false
。
// Accept-Language: en;q=0.8, es, pt ctx.acceptsLanguages('es', 'en'); // => "es" ctx.acceptsLanguages(['en', 'es']); // => "es"
当没有参数被赋予全部接受的语言将做为数组返回:
// Accept-Language: en;q=0.8, es, pt ctx.acceptsLanguages(); // => ["es", "pt", "en"]
检查请求是不是幂等的。
返回请求套接字。
返回请求标头。
Koa Response
对象是在 node 的 vanilla 响应对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。
响应标头对象。
响应标头对象。别名是 response.header
。
请求套接字。
获取响应状态。默认状况下,response.status
设置为 404
而不是像 node 的 res.statusCode
那样默认为 200
。
经过数字代码设置响应状态:
注意: 不用太在乎记住这些字符串, 若是你写错了,能够查阅这个列表随时更正.
获取响应的状态消息. 默认状况下, response.message
与 response.status
关联.
将响应的状态消息设置为给定值。
将响应的 Content-Length 设置为给定值。
以数字返回响应的 Content-Length,或者从ctx.body
推导出来,或者undefined
。
获取响应主体。
将响应体设置为如下之一:
string
写入Buffer
写入Stream
管道Object
|| Array
JSON-字符串化null
无内容响应若是 response.status
未被设置, Koa 将会自动设置状态为 200
或 204
。
Content-Type 默认为 text/html
或 text/plain
, 同时默认字符集是 utf-8。Content-Length 字段也是如此。
Content-Type 默认为 application/octet-stream
, 而且 Content-Length 字段也是如此。
Content-Type 默认为 application/octet-stream
。
每当流被设置为响应主体时,.onerror
做为侦听器自动添加到 error
事件中以捕获任何错误。此外,每当请求关闭(甚至过早)时,流都将被销毁。若是你不想要这两个功能,请勿直接将流设为主体。例如,当将主体设置为代理中的 HTTP 流时,你可能不想要这样作,由于它会破坏底层链接。
参阅: https://github.com/koajs/koa/pull/612 获取更多信息。
如下是流错误处理的示例,而不会自动破坏流:
const PassThrough = require('stream').PassThrough; app.use(async ctx => { ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough()); });
Content-Type 默认为 application/json
. 这包括普通的对象 { foo: 'bar' }
和数组 ['foo', 'bar']
。
不区分大小写获取响应标头字段值 field
。
const etag = ctx.response.get('ETag');
设置响应标头 field
到 value
:
ctx.set('Cache-Control', 'no-cache');
用值 val
附加额外的标头 field
。
ctx.append('Link', '<http://127.0.0.1/>');
用一个对象设置多个响应标头fields
:
ctx.set({
'Etag': '1234', 'Last-Modified': date });
删除标头 field
。
获取响应 Content-Type
不含参数 "charset"。
const ct = ctx.type; // => "image/png"
设置响应 Content-Type
经过 mime 字符串或文件扩展名。
ctx.type = 'text/plain; charset=utf-8'; ctx.type = 'image/png'; ctx.type = '.png'; ctx.type = 'png';
注意: 在适当的状况下为你选择 charset
, 好比 response.type = 'html'
将默认是 "utf-8". 若是你想覆盖 charset
, 使用 ctx.set('Content-Type', 'text/html')
将响应头字段设置为直接值。
很是相似 ctx.request.is()
. 检查响应类型是不是所提供的类型之一。这对于建立操纵响应的中间件特别有用。
例如, 这是一个中间件,能够削减除流以外的全部HTML响应。
const minify = require('html-minifier'); app.use(async (ctx, next) => { await next(); if (!ctx.response.is('html')) return; let body = ctx.body; if (!body || body.pipe) return; if (Buffer.isBuffer(body)) body = body.toString(); ctx.body = minify(body); });
执行 [302] 重定向到 url
.
字符串 “back” 是特别提供Referrer支持的,当Referrer不存在时,使用 alt
或“/”。
ctx.redirect('back'); ctx.redirect('back', '/index.html'); ctx.redirect('/login'); ctx.redirect('http://google.com');
要更改 “302” 的默认状态,只需在该调用以前或以后分配状态。要变动主体请在此调用以后:
ctx.status = 301; ctx.redirect('/cart'); ctx.body = 'Redirecting to shopping cart';
将 Content-Disposition
设置为 “附件” 以指示客户端提示下载。(可选)指定下载的 filename
。
检查是否已经发送了一个响应头。 用于查看客户端是否可能会收到错误通知。
将 Last-Modified
标头返回为 Date
, 若是存在。
将 Last-Modified
标头设置为适当的 UTC 字符串。您能够将其设置为 Date
或日期字符串。
ctx.response.lastModified = new Date();
设置包含 "
包裹的 ETag 响应, 请注意,没有相应的 response.etag
getter。
ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
在 field
上变化。
刷新任何设置的标头,并开始主体。
若是您但愿贡献力量完善本中文文档,请前往 https://github.com/demopark/koa-docs-Zh-CN.git 仓库。
Community links to discover third-party middleware for Koa, full runnable examples, thorough guides and more! If you have questions join us in IRC!