用于 node.js 的下一代 Web 框架

简介

Koa 是由 Express 团队设计的新 Web 框架,旨在为 Web 应用和 API 提供更小、更具表现力且更强大的基础。通过利用异步函数,Koa 允许您摆脱回调并大大增强错误处理能力。Koa 的核心不捆绑任何中间件,它提供了一套优雅的方法,使编写服务器变得快速而愉快。

安装

Koa 需要 node v12 或更高版本才能支持 ES2015 和异步函数。

您可以使用您喜欢的版本管理器快速安装受支持的 node 版本

$ nvm install 7
$ npm i koa
$ node my-koa-app.js

应用

Koa 应用是一个对象,其中包含一个中间件函数数组,这些函数在请求时以类似堆栈的方式组合和执行。Koa 类似于您可能遇到过的许多其他中间件系统,例如 Ruby 的 Rack、Connect 等等 - 然而,一个关键的设计决策是在低级中间件层提供高级“语法糖”。这提高了互操作性和健壮性,并使编写中间件更加愉快。

这包括用于常见任务的方法,例如内容协商、缓存新鲜度、代理支持和重定向等。尽管提供了大量有用的方法,但 Koa 仍保持较小的占用空间,因为它没有捆绑任何中间件。

强制性的“Hello World”应用

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

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

级联

Koa 中间件以一种您可能习惯于类似工具的更传统的方式进行级联 - 以前,由于 node 使用回调,这很难做到用户友好。但是,使用异步函数,我们可以实现“真正的”中间件。与 Connect 的实现(它只是将控制权传递给一系列函数,直到一个函数返回)形成对比,Koa 调用“下游”,然后控制权“上游”返回。

以下示例响应“Hello World”,但是首先请求流经 x-response-timelogging 中间件以标记请求何时开始,然后通过响应中间件产生控制权。当中间件调用 next() 时,该函数挂起并将控制权传递给定义的下一个中间件。在没有更多中间件要向下游执行之后,堆栈将展开,并且每个中间件都将恢复以执行其上游行为。

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

// logger

app.use(async (ctx, next) => {
  await next();
  const rt = ctx.response.get('X-Response-Time');
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});

// 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`);
});

// response

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

设置

应用设置是 app 实例上的属性,当前支持以下设置

app.listen(...)

Koa 应用不是 HTTP 服务器的一对一表示。一个或多个 Koa 应用可以一起挂载以形成更大的应用,并使用单个 HTTP 服务器。

创建并返回一个 HTTP 服务器,将给定的参数传递给 Server#listen()。这些参数记录在 nodejs.org 上。以下是一个绑定到端口 3000 的无用 Koa 应用

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);

app.callback()

返回一个回调函数,该函数适用于 http.createServer() 方法来处理请求。您也可以使用此回调函数将您的 Koa 应用挂载到 Connect/Express 应用中。

app.use(function)

将给定的中间件函数添加到此应用。app.use() 返回 this,因此是可链接的。

app.use(someMiddleware)
app.use(someOtherMiddleware)
app.listen(3000)

与以下内容相同

app.use(someMiddleware)
  .use(someOtherMiddleware)
  .listen(3000)

有关更多信息,请参阅 中间件

app.keys=

设置已签名的 Cookie 密钥。

这些将传递给 KeyGrip,但是您也可以传递您自己的 KeyGrip 实例。例如,以下是可以接受的

app.keys = ['OEK5zjaAMPc3L6iK7PyUjCOziUH3rsrMKB9u8H07La1SkfwtuBoDnHaaPCkG5Brg', 'MNKeIebviQnCPo38ufHcSfw3FFv8EtnAe1xE02xkN1wkCV1B2z126U44yk2BQVK7'];
app.keys = new KeyGrip(['OEK5zjaAMPc3L6iK7PyUjCOziUH3rsrMKB9u8H07La1SkfwtuBoDnHaaPCkG5Brg', 'MNKeIebviQnCPo38ufHcSfw3FFv8EtnAe1xE02xkN1wkCV1B2z126U44yk2BQVK7'], 'sha256');

出于安全原因,请确保密钥足够长且随机。

这些密钥可以轮换,并在使用 { signed: true } 选项签名 Cookie 时使用

ctx.cookies.set('name', 'tobi', { signed: true });

app.context

app.context 是从中创建 ctx 的原型。您可以通过编辑 app.contextctx 添加其他属性。这对于向 ctx 添加要在整个应用中使用的属性或方法很有用,这可能会提高性能(无中间件)和/或更容易(更少的 require()),但代价是更多地依赖 ctx,这可能被认为是一种反模式。

例如,要从 ctx 添加对数据库的引用

app.context.db = db();

app.use(async ctx => {
  console.log(ctx.db);
});

注意

错误处理

默认情况下,除非 app.silenttrue,否则将所有错误输出到 stderr。当 err.status404err.exposetrue 时,默认错误处理程序也不会输出错误。要执行自定义错误处理逻辑(例如集中式日志记录),您可以添加“error”事件侦听器

app.on('error', err => {
  log.error('server error', err)
});

如果在 req/res 周期中出现错误,并且无法响应客户端,则还会传递 Context 实例

app.on('error', (err, ctx) => {
  log.error('server error', err, ctx)
});

当发生错误并且仍然可以响应客户端时,也就是没有数据写入套接字,Koa 将使用 500“内部服务器错误”进行适当的响应。无论哪种情况,都会发出应用级“错误”以进行日志记录。

上下文

Koa 上下文将 node 的 requestresponse 对象封装到一个对象中,该对象提供了许多用于编写 Web 应用和 API 的有用方法。这些操作在 HTTP 服务器开发中使用得如此频繁,以至于它们被添加到此级别而不是更高级别的框架中,这将迫使中间件重新实现此通用功能。

每个请求都会创建一个 Context,并在中间件中将其引用为接收器或 ctx 标识符,如以下代码段所示

app.use(async ctx => {
  ctx; // is the Context
  ctx.request; // is a Koa Request
  ctx.response; // is a Koa Response
});

许多上下文的访问器和方法只是为了方便起见而委托给它们的 ctx.requestctx.response 等效项,否则它们是相同的。例如,ctx.typectx.length 委托给 response 对象,而 ctx.pathctx.method 委托给 request

API

Context 特定的方法和访问器。

ctx.req

Node 的 request 对象。

ctx.res

Node 的 response 对象。

不支持绕过 Koa 的响应处理。避免使用以下 node 属性

ctx.request

Koa Request 对象。

ctx.response

Koa Response 对象。

ctx.state

推荐的命名空间,用于通过中间件和您的前端视图传递信息。

ctx.state.user = await User.find(id);

ctx.app

应用实例引用。

ctx.app.emit

Koa 应用扩展了内部 EventEmitterctx.app.emit 使用由第一个参数定义的类型发出事件。对于每个事件,您都可以挂钩“侦听器”,它是在发出事件时调用的函数。有关更多信息,请参阅 错误处理文档

ctx.cookies.get(name, [options])

使用 options 获取 Cookie name

Koa 使用 cookies 模块,其中选项只是传递的。

ctx.cookies.set(name, value, [options])

使用 options 将 Cookie name 设置为 value

Koa 使用 cookies 模块,其中选项只是传递的。

ctx.throw([status], [msg], [properties])

帮助器方法,用于抛出一个具有 .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 来创建错误。status 只能作为第一个参数传递。

ctx.assert(value, [status], [msg], [properties])

帮助器方法,用于在 !value 时抛出一个类似于 .throw() 的错误。类似于 node 的 assert() 方法。

ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

Koa 使用 http-assert 进行断言。

ctx.respond

要绕过 Koa 的内置响应处理,您可以显式设置 ctx.respond = false;。如果您想写入原始 res 对象而不是让 Koa 为您处理响应,请使用此选项。

请注意,Koa 不支持使用此选项。这可能会破坏 Koa 中间件和 Koa 本身的预期功能。使用此属性被认为是一种 hack,并且只是为了方便那些希望在 Koa 中使用传统的 fn(req, res) 函数和中间件的人。

请求别名

以下访问器和别名 请求 等效项

响应别名

以下访问器和别名 响应 等效项

请求

Koa 的 Request 对象是对 Node.js 原生请求对象的抽象,提供了对日常 HTTP 服务器开发有用的附加功能。

API

request.header

请求头对象。这与 Node.js 的 http.IncomingMessage 上的 headers 字段相同。

request.header=

设置请求头对象。

request.headers

请求头对象。与 request.header 相同。

request.headers=

设置请求头对象。与 request.header= 相同。

request.method

请求方法。

request.method=

设置请求方法,用于实现 methodOverride() 等中间件。

request.length

返回请求 Content-Length 的数字形式(如果存在),否则返回 undefined

request.url

获取请求 URL。

request.url=

设置请求 URL,用于 URL 重写。

request.originalUrl

获取请求原始 URL。

request.origin

获取 URL 的来源,包括 protocolhost

ctx.request.origin
// => http://example.com

request.href

获取完整的请求 URL,包括 protocolhosturl

ctx.request.href;
// => http://example.com/foo/bar?q=1

request.path

获取请求路径名。

request.path=

设置请求路径名,并在存在查询字符串时保留它。

request.querystring

获取不带 ? 的原始查询字符串。

request.querystring=

设置原始查询字符串。

获取带 ? 的原始查询字符串。

request.search=

设置原始查询字符串。

request.host

获取主机名(主机名:端口)(如果存在)。当 app.proxytrue 时,支持 X-Forwarded-Host,否则使用 Host

request.hostname

获取主机名(如果存在)。当 app.proxytrue 时,支持 X-Forwarded-Host,否则使用 Host

如果主机是 IPv6,Koa 会将解析委托给 WHATWG URL API注意 这可能会影响性能。

request.URL

获取 WHATWG 解析的 URL 对象。

request.type

获取不带参数(如“charset”)的请求 Content-Type

const ct = ctx.request.type;
// => "image/png"

request.charset

获取请求字符集(如果存在),否则返回 undefined

ctx.request.charset;
// => "utf-8"

request.query

获取解析后的查询字符串,如果不存在查询字符串,则返回一个空对象。请注意,此 getter 支持嵌套解析。

例如“color=blue&size=small”。

{
  color: 'blue',
  size: 'small'
}

request.query=

将查询字符串设置为给定对象。请注意,此 setter 支持嵌套对象。

ctx.query = { next: '/login' };

request.fresh

检查请求缓存是否“新鲜”,即内容是否已更改。此方法用于 If-None-Match / ETagIf-Modified-SinceLast-Modified 之间的缓存协商。它应该在设置一个或多个响应头之后被引用。

// freshness check requires status 20x or 304
ctx.status = 200;
ctx.set('ETag', '123');

// cache is ok
if (ctx.fresh) {
  ctx.status = 304;
  return;
}

// cache is stale
// fetch new data
ctx.body = await db.find('something');

request.stale

request.fresh 相反。

request.protocol

返回请求协议,“https”或“http”。当 app.proxytrue 时,支持 X-Forwarded-Proto

request.secure

ctx.protocol == "https" 的简写形式,用于检查请求是否通过 TLS 发出。

request.ip

请求远程地址。当 app.proxytrue 时,支持 X-Forwarded-For

request.ips

X-Forwarded-For 存在且 app.proxy 启用时,将返回这些 IP 地址的数组,按上游 -> 下游的顺序排列。禁用时,将返回一个空数组。

例如,如果值为“client, proxy1, proxy2”,您将收到数组 ["client", "proxy1", "proxy2"]

大多数反向代理(nginx)通过 proxy_add_x_forwarded_for 设置 x-forwarded-for,这构成了一定的安全风险。恶意攻击者可以通过伪造 X-Forwarded-For 请求头来伪造客户端的 IP 地址。客户端发送的请求包含一个值为“forged”的 X-Forwarded-For 请求头。在被反向代理转发后,request.ips 将是 ['forged', 'client', 'proxy1', 'proxy2']。

Koa 提供了两种选择来避免被绕过。

如果您可以控制反向代理,则可以通过调整配置来避免绕过,或者使用 koa 提供的 app.proxyIpHeader 来避免读取 x-forwarded-for 来获取 IP 地址。

  const app = new Koa({
    proxy: true,
    proxyIpHeader: 'X-Real-IP',
  });

如果您确切知道服务器前面有多少个反向代理,则可以通过配置 app.maxIpsCount 来避免读取用户伪造的请求头。

  const app = new Koa({
    proxy: true,
    maxIpsCount: 1, // only one proxy in front of the server
  });

  // request.header['X-Forwarded-For'] === [ '127.0.0.1', '127.0.0.2' ];
  // ctx.ips === [ '127.0.0.2' ];

request.subdomains

以数组形式返回子域名。

子域名是应用程序主域名之前、以点分隔的主机名部分。默认情况下,应用程序的域名假定为主机名的最后两部分。这可以通过设置 app.subdomainOffset 来更改。

例如,如果域名为“tobi.ferrets.example.com”:如果未设置 app.subdomainOffset,则 ctx.subdomains["ferrets", "tobi"]。如果 app.subdomainOffset 为 3,则 ctx.subdomains["tobi"]

request.is(types...)

检查传入请求是否包含“Content-Type”头字段,以及它是否包含任何给定的 MIME type。如果没有请求正文,则返回 null。如果没有内容类型或匹配失败,则返回 false。否则,它将返回匹配的内容类型。

// With Content-Type: text/html; charset=utf-8
ctx.is('html'); // => 'html'
ctx.is('text/html'); // => 'text/html'
ctx.is('text/*', 'text/html'); // => 'text/html'

// When Content-Type is 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/*')) {
  // process
} else {
  ctx.throw(415, 'images only!');
}

内容协商

Koa 的 request 对象包含由 acceptsnegotiator 提供支持的有用的内容协商实用程序。这些实用程序是

如果没有提供类型,则返回 所有 可接受的类型。

如果提供了多种类型,则将返回最佳匹配。如果没有找到匹配项,则返回 false,您应该向客户端发送 406 "Not Acceptable" 响应。

在缺少接受头且任何类型都可以接受的情况下,将返回第一种类型。因此,您提供的类型顺序很重要。

request.accepts(types)

检查给定的 type(s) 是否可接受,如果可接受则返回最佳匹配,否则返回 falsetype 值可以是一个或多个 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');
}

request.acceptsEncodings(encodings)

检查 encodings 是否可接受,如果可接受则返回最佳匹配,否则返回 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 的情况。

request.acceptsCharsets(charsets)

检查 charsets 是否可接受,如果可接受则返回最佳匹配,否则返回 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"]

request.acceptsLanguages(langs)

检查 langs 是否可接受,如果可接受则返回最佳匹配,否则返回 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"]

request.idempotent

检查请求是否是幂等的。

request.socket

返回请求套接字。

request.get(field)

返回不区分大小写的 field 的请求头。

响应

Koa 的 Response 对象是对 Node.js 原生响应对象的抽象,提供了对日常 HTTP 服务器开发有用的附加功能。

API

response.header

响应头对象。

response.headers

响应头对象。与 response.header 相同。

response.socket

响应套接字。指向 net.Socket 实例,与 request.socket 相同。

response.status

获取响应状态。默认情况下,response.status 设置为 404,这与 Node.js 的 res.statusCode(默认为 200)不同。

response.status=

通过数字代码设置响应状态

注意:不必过多担心记住这些字符串,如果您输入错误,将会抛出一个错误,并显示此列表,以便您进行更正。

由于 response.status 默认为 404,因此要发送没有正文且状态不同的响应,请执行以下操作

ctx.response.status = 200;

// Or whatever other status
ctx.response.status = 204;

response.message

获取响应状态消息。默认情况下,response.messageresponse.status 相关联。

response.message=

将响应状态消息设置为给定值。

response.length=

将响应 Content-Length 设置为给定值。

response.length

返回响应 Content-Length 的数字形式(如果存在),如果可能,则从 ctx.body 推断,否则返回 undefined

response.body

获取响应正文。

response.body=

将响应正文设置为以下之一

如果尚未设置 response.status,Koa 将根据 response.body 自动将状态设置为 200204。具体来说,如果尚未设置 response.body 或已将其设置为 nullundefined,则 Koa 将自动将 response.status 设置为 204。如果您确实要发送没有内容且状态不同的响应,则应按以下方式覆盖 204 状态

// This must be always set first before status, since null | undefined
// body automatically sets the status to 204
ctx.body = null;
// Now we override the 204 status with the desired one
ctx.status = 200;

Koa 不会阻止任何可以作为响应正文的内容 - 函数不会有意义地序列化,返回布尔值可能对您的应用程序有意义,并且虽然错误有效,但它可能无法按预期工作,因为错误的某些属性不可枚举。我们建议在您的应用程序中添加中间件,以根据每个应用程序断言正文类型。示例中间件可能是

app.use(async (ctx, next) => {
  await next()

  ctx.assert.equal('object', typeof ctx.body, 500, 'some dev did something wrong')
})

字符串

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', (err) => ctx.onerror(err)).pipe(PassThrough());
});

对象

内容类型默认为 application/json。这包括普通对象 { foo: 'bar' } 和数组 ['foo', 'bar']

response.get(field)

使用不区分大小写的 field 获取响应头字段值。

const etag = ctx.response.get('ETag');

response.has(field)

如果由名称标识的标头当前设置在传出标头中,则返回 true。标头名称匹配不区分大小写。

const rateLimited = ctx.response.has('X-RateLimit-Limit');

response.set(field, value)

将响应头 field 设置为 value

ctx.set('Cache-Control', 'no-cache');

response.append(field, value)

使用值 val 追加额外的标头 field

ctx.append('Link', '<http://127.0.0.1/>');

response.set(fields)

使用对象设置多个响应头 fields

ctx.set({
  'Etag': '1234',
  'Last-Modified': date
});

这将委托给 setHeader,它通过指定的键设置或更新标头,并且不会重置整个标头。

response.remove(field)

移除标头 field

response.type

获取不带参数(例如“charset”)的响应 Content-Type

const ct = ctx.type;
// => "image/png"

response.type=

通过 mime 字符串或文件扩展名设置响应 Content-Type

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') 直接将响应头字段设置为值。

response.is(types...)

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);
});

response.redirect(url, [alt])

执行 [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';

response.attachment([filename], [options])

Content-Disposition 设置为“attachment”,以指示客户端提示下载。可以选择指定下载的 filename 和一些 选项

response.headerSent

检查是否已发送响应头。用于查看是否可以在出错时通知客户端。

response.lastModified

如果存在,则以 Date 的形式返回 Last-Modified 标头。

response.lastModified=

Last-Modified 标头设置为适当的 UTC 字符串。您可以将其设置为 Date 或日期字符串。

ctx.response.lastModified = new Date();

response.etag=

设置响应的 ETag,包括包装的 "。请注意,没有对应的 response.etag getter。

ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

response.vary(field)

field 上进行变化。

response.flushHeaders()

刷新任何设置的标头,并开始正文。

赞助商

Apex Ping 是由 Koa 的一位原作者创建的,适用于网站和 API 的美观的正常运行时间监控解决方案。

链接

社区链接,用于发现 Koa 的第三方中间件、完整的可运行示例、详尽的指南等等!如果您有任何问题,请加入我们的 IRC!