基於Node.js 平臺的下一代web開發框架
簡介
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 應用擴展了內部 EventEmitter。ctx.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,包括 protocol
,host
和 url
。
ctx.request.href;
// => http://example.com/foo/bar?q=1
request.path
獲取請求路徑名。
request.path=
設置請求路徑名,並在存在時保留查詢字符串。
request.querystring
根據 ?
獲取原始查詢字符串.
request.querystring=
設置原始查詢字符串。
request.search
使用 ?
獲取原始查詢字符串。
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)。