koa2學習記錄一下

基於Node.js 平臺的下一代web開發框架

官網文檔 https://koa.bootcss.com/

簡介

Koa 是一個新的 web 框架,由 Express 幕後的原班人馬打造, 致力於成爲 web 應用和 API 開發領域中的一個更小、更富有表現力、更健壯的基石。 通過利用 async 函數,Koa 幫你丟棄回調函數,並有力地增強錯誤處理。 Koa 並沒有捆綁任何中間件, 而是提供了一套優雅的方法,幫助您快速而愉快地編寫服務端應用程序。

安裝

Koa 依賴 node v7.6.0 或 ES2015及更高版本和 async 方法支持.

你可以使用自己喜歡的版本管理器快速安裝支持的 node 版本:

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

使用 Babel 實現 Async 方法

要在 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 插件.

例如, 在你的 .babelrc 文件中, 你應該有:

{
  "plugins": ["transform-async-to-generator"]
}

你也可以用 env preset 的 target 參數 "node": "current" 替代.

應用程序

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 的回調。然而,使用 async 功能,我們可以實現 “真實” 的中間件。對比 Connect 的實現,通過一系列功能直接傳遞控制,直到一個返回,Koa 調用“下游”,然後控制流回“上游”。

下面以 “Hello World” 的響應作爲示例,當請求開始時首先請求流通過 x-response-time 和 logging 中間件,然後繼續移交控制給 response 中間件。當一箇中間件調用 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.env 默認是 NODE_ENV 或 "development"
  • app.keys 簽名的 cookie 密鑰數組
  • app.proxy 當真正的代理頭字段將被信任時
  • 忽略 .subdomains 的 app.subdomainOffset 偏移量,默認爲 2
  • app.proxyIpHeader 代理 ip 消息頭, 默認爲 X-Forwarded-For
  • app.maxIpsCount 從代理 ip 消息頭讀取的最大 ips, 默認爲 0 (代表無限)

您可以將設置傳遞給構造函數:

  const Koa = require('koa');
  const app = new Koa({ proxy: true });

或動態的:

  const Koa = require('koa');
  const app = new Koa();
  app.proxy = true;

app.listen(...)

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

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)

參閱 Middleware 獲取更多信息.

app.keys=

設置簽名的 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

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 “內部服務器錯誤” 進行適當的響應。在任一情況下,爲了記錄目的,都會發出應用級 “錯誤”。

上下文(Context)

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

API

Context 具體方法和訪問器.

ctx.req

Node 的 request 對象.

ctx.res

Node 的 response 對象.

繞過 Koa 的 response 處理是 不被支持的. 應避免使用以下 node 屬性:

  • res.statusCode
  • res.writeHead()
  • res.write()
  • res.end()

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 發出一個類型由第一個參數定義的事件。對於每個事件,您可以連接 "listeners",這是在發出事件時調用的函數。有關更多信息,請參閱錯誤處理文檔

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

通過 options 獲取 cookie name:

  • signed 所請求的cookie應該被簽名

koa 使用 cookies 模塊,其中只需傳遞參數。

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

通過 options 設置 cookie name 的 value :

  • maxAge: 一個數字, 表示從 Date.now() 得到的毫秒數.
    • expires: 一個 Date 對象, 表示 cookie 的到期日期 (默認情況下在會話結束時過期).
    • path: 一個字符串, 表示 cookie 的路徑 (默認是/).
    • domain: 一個字符串, 指示 cookie 的域 (無默認值).
    • secure: 一個布爾值, 表示 cookie 是否僅通過 HTTPS 發送 (HTTP 下默認爲 false, HTTPS 下默認爲 true). 閱讀有關此參數的更多信息.
    • httpOnly: 一個布爾值, 表示 cookie 是否僅通過 HTTP(S) 發送,, 且不提供給客戶端 JavaScript (默認爲 true).
    • sameSite: 一個布爾值或字符串, 表示該 cookie 是否爲 "相同站點" cookie (默認爲 false). 可以設置爲 'strict''lax''none', 或 true (映射爲 'strict').
    • signed: 一個布爾值, 表示是否要對 cookie 進行簽名 (默認爲 false). 如果爲 true, 則還會發送另一個後綴爲 .sig 的同名 cookie, 使用一個 27-byte url-safe base64 SHA1 值來表示針對第一個 Keygrip 鍵的 cookie-name=cookie-value 的哈希值. 此簽名密鑰用於檢測下次接收 cookie 時的篡改.
    • overwrite: 一個布爾值, 表示是否覆蓋以前設置的同名的 cookie (默認是 false). 如果是 true, 在同一個請求中設置相同名稱的所有 Cookie(無論路徑或域)是否在設置此Cookie 時從 Set-Cookie 消息頭中過濾掉.

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 的內置 response 處理,你可以顯式設置 ctx.respond = false;。 如果您想要寫入原始的 res 對象而不是讓 Koa 處理你的 response,請使用此參數。

請注意,Koa _不_ 支持使用此功能。這可能會破壞 Koa 中間件和 Koa 本身的預期功能。使用這個屬性被認爲是一個 hack,只是便於那些希望在 Koa 中使用傳統的 fn(req, res) 功能和中間件的人。

Request 別名

以下訪問器和 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 別名

以下訪問器和 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=

請求(Request)

Koa Request 對象是在 node 的 原生請求對象之上的抽象,提供了諸多對 HTTP 服務器開發有用的功能。

API

request.header

請求頭對象。這與 node 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的來源,包括 protocol 和 host

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

request.href

獲取完整的請求URL,包括 protocolhost 和 url

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

request.path

獲取請求路徑名。

request.path=

設置請求路徑名,並在存在時保留查詢字符串。

request.querystring

根據 ? 獲取原始查詢字符串.

request.querystring=

設置原始查詢字符串。

使用 ? 獲取原始查詢字符串。

request.search=

設置原始查詢字符串。

request.host

存在時獲取主機(hostname:port)。當 app.proxy 是 true 時支持 X-Forwarded-Host,否則使用 Host

request.hostname

存在時獲取主機名。當 app.proxy 是 true 時支持 X-Forwarded-Host,否則使用 Host

如果主機是 IPv6, Koa 解析到 WHATWG URL API注意 這可能會影響性能。

request.URL

獲取 WHATWG 解析的 URL 對象。

request.type

獲取請求 Content-Type, 不含 "charset" 等參數。

譯者注: 這裏其實是隻獲取 mime-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 / 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.stale

與 request.fresh 相反.

request.protocol

返回請求協議,“https” 或 “http”。當 app.proxy 是 true 時支持 X-Forwarded-Proto

request.secure

通過 ctx.protocol == "https" 來檢查請求是否通過 TLS 發出。

request.ip

請求遠程地址。 當 app.proxy 是 true 時支持 X-Forwarded-Proto

request.ips

當 X-Forwarded-For 存在並且 app.proxy 被啓用時,這些 ips 的數組被返回,從上游 - >下游排序。 禁用時返回一個空數組。

例如,如果值是 "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 獲取 ips。

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

如果您確切知道服務器前面有多少個反向代理,則可以通過配置 app.maxIpsCount 來避免讀取用戶的僞造的請求頭:

const app = new Koa({
  proxy: true,
  maxIpsCount: 1, // 服務器前只有一個代理
});

// 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。 反之則返回匹配的 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" 響應。

如果接收到任何類型的接收頭,則會返回第一個類型。 因此,你提供的類型的順序很重要。

request.accepts(types)

檢查給定的 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');
}

request.acceptsEncodings(encodings)

檢查 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 的情況。

request.acceptsCharsets(charsets)

檢查 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"]

request.acceptsLanguages(langs)

檢查 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"]

request.idempotent

檢查請求是否是冪等的。

request.socket

返回請求套接字。

request.get(field)

返回請求頭(header), field 不區分大小寫.

響應(Response)

Koa Response 對象是在 node 的原生響應對象之上的抽象,提供了諸多對 HTTP 服務器開發有用的功能。

API

response.header

響應頭對象。

response.headers

響應頭對象。別名是 response.header

response.socket

響應套接字。 作爲 request.socket 指向 net.Socket 實例。

response.status

獲取響應狀態。默認情況下,response.status 設置爲 404 而不是像 node 的 res.statusCode 那樣默認爲 200

response.status=

通過數字代碼設置響應狀態:

  • 100 "continue"
  • 101 "switching protocols"
  • 102 "processing"
  • 200 "ok"
  • 201 "created"
  • 202 "accepted"
  • 203 "non-authoritative information"
  • 204 "no content"
  • 205 "reset content"
  • 206 "partial content"
  • 207 "multi-status"
  • 208 "already reported"
  • 226 "im used"
  • 300 "multiple choices"
  • 301 "moved permanently"
  • 302 "found"
  • 303 "see other"
  • 304 "not modified"
  • 305 "use proxy"
  • 307 "temporary redirect"
  • 308 "permanent redirect"
  • 400 "bad request"
  • 401 "unauthorized"
  • 402 "payment required"
  • 403 "forbidden"
  • 404 "not found"
  • 405 "method not allowed"
  • 406 "not acceptable"
  • 407 "proxy authentication required"
  • 408 "request timeout"
  • 409 "conflict"
  • 410 "gone"
  • 411 "length required"
  • 412 "precondition failed"
  • 413 "payload too large"
  • 414 "uri too long"
  • 415 "unsupported media type"
  • 416 "range not satisfiable"
  • 417 "expectation failed"
  • 418 "I'm a teapot"
  • 422 "unprocessable entity"
  • 423 "locked"
  • 424 "failed dependency"
  • 426 "upgrade required"
  • 428 "precondition required"
  • 429 "too many requests"
  • 431 "request header fields too large"
  • 500 "internal server error"
  • 501 "not implemented"
  • 502 "bad gateway"
  • 503 "service unavailable"
  • 504 "gateway timeout"
  • 505 "http version not supported"
  • 506 "variant also negotiates"
  • 507 "insufficient storage"
  • 508 "loop detected"
  • 510 "not extended"
  • 511 "network authentication required"

注意: 不用太在意記住這些字符串, 如果你寫錯了,可以查閱這個列表隨時更正.

由於 response.status 默認設置爲 404,因此發送沒有 body 且狀態不同的響應的操作如下:

ctx.response.status = 200;

// 或其他任何狀態
ctx.response.status = 204;

response.message

獲取響應的狀態消息. 默認情況下, response.message 與 response.status 關聯.

response.message=

將響應的狀態消息設置爲給定值。

response.length=

將響應的 Content-Length 設置爲給定值。

response.length

以數字返回響應的 Content-Length,或者從ctx.body推導出來,或者undefined

response.body

獲取響應主體。

response.body=

將響應體設置爲以下之一:

  • string 寫入
  • Buffer 寫入
  • Stream 管道
  • Object || Array JSON-字符串化
  • null 無內容響應

如果 response.status 未被設置, Koa 將會自動設置狀態爲 200 或 204

Koa 沒有防範作爲響應體的所有內容 - 函數沒有有意義地序列化,返回布爾值可能會根據您的應用程序而有意義。並且當錯誤生效時,它可能無法正常工作 錯誤的屬性無法枚舉。 我們建議在您的應用中添加中間件,以確定每個應用的正文類型。 示例中間件可能是:

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

  ctx.assert.equal('object', typeof ctx, 500, '某些開發錯誤')
})

String

Content-Type 默認爲 text/html 或 text/plain, 同時默認字符集是 utf-8。Content-Length 字段也是如此。

Buffer

Content-Type 默認爲 application/octet-stream, 並且 Content-Length 字段也是如此。

Stream

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

Object

Content-Type 默認爲 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

獲取響應 Content-Type, 不含 "charset" 等參數。

譯者注: 這裏其實是隻獲取 mime-type, 詳見源碼及其註釋

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

response.type=

設置響應 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') 將響應頭字段設置爲直接值。

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” 的默認狀態,只需在該調用之前或之後給 status 賦值。要變更主體請在此調用之後:

ctx.status = 301;
ctx.redirect('/cart');
ctx.body = 'Redirecting to shopping cart';

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

將 Content-Disposition 設置爲 “附件” 以指示客戶端提示下載。(可選)指定下載的 filename 和部分 參數

response.headerSent

檢查是否已經發送了一個響應頭。 用於查看客戶端是否可能會收到錯誤通知。

response.lastModified

將 Last-Modified 消息頭返回爲 Date, 如果存在。

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 的 vary

response.flushHeaders()

刷新任何設置的消息頭,然後是主體(body)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章