簡介
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!