koa 是由 Express 原班人馬打造的,致力於成爲一個更小、更富有表現力、更健壯的 Web 框架。使用 koa 編寫 web 應用,經過組合不一樣的 generator,能夠免除重複繁瑣的回調函數嵌套,並極大地提高錯誤處理的效率。koa 不在內核方法中綁定任何中間件,它僅僅提供了一個輕量優雅的函數庫,使得編寫 Web 應用變得駕輕就熟。css
Koa 目前須要 >=0.11.x版本的 node 環境。並須要在執行 node 的時候附帶 --harmony 來引入 generators 。 若是您安裝了較舊版本的 node ,您能夠安裝 n (node版本控制器),來快速安裝 0.11.xhtml
$ npm install -g n $ n 0.11.12 $ node --harmony my-koa-app.js
Koa 應用是一個包含一系列中間件 generator 函數的對象。 這些中間件函數基於 request 請求以一個相似於棧的結構組成並依次執行。 Koa 相似於其餘中間件系統(好比 Ruby's Rack 、Connect 等), 然而 Koa 的核心設計思路是爲中間件層提供高級語法糖封裝,以加強其互用性和健壯性,並使得編寫中間件變得至關有趣。node
Koa 包含了像 content-negotiation(內容協商)、cache freshness(緩存刷新)、proxy support(代理支持)和 redirection(重定向)等經常使用任務方法。 與提供龐大的函數支持不一樣,Koa只包含很小的一部分,由於Koa並不綁定任何中間件。git
任何教程都是從 hello world 開始的,Koa也不例外^_^github
var koa = require('koa'); var app = koa(); app.use(function *(){ this.body = 'Hello World'; }); app.listen(3000);
Koa 的中間件經過一種更加傳統(您也許會很熟悉)的方式進行級聯,摒棄了以往 node 頻繁的回調函數形成的複雜代碼邏輯。 咱們經過 generators 來實現「真正」的中間件。 Connect 簡單地將控制權交給一系列函數來處理,直到函數返回。 與之不一樣,當執行到 yield next
語句時,Koa 暫停了該中間件,繼續執行下一個符合請求的中間件('downstrem'),而後控制權再逐級返回給上層中間件('upstream')。web
下面的例子在頁面中返回 "Hello World",然而當請求開始時,請求先通過 x-response-time
和logging
中間件,並記錄中間件執行起始時間。 而後將控制權交給 reponse
中間件。當中間件運行到yield next
時,函數掛起並將控制前交給下一個中間件。當沒有中間件執行 yield next
時,程序棧會逆序喚起被掛起的中間件來執行接下來的代碼。express
var koa = require('koa'); var app = koa(); // x-response-time app.use(function *(next){ var start = new Date; yield next; var ms = new Date - start; this.set('X-Response-Time', ms + 'ms'); }); // logger app.use(function *(next){ var start = new Date; yield next; var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); }); // response app.use(function *(){ this.body = 'Hello World'; }); app.listen(3000);
應用配置是 app
實例屬性,目前支持的配置項以下:npm
app.name
應用名稱(可選項)json
app.env
默認爲 NODE_ENV 或者 development
api
app.proxy
若是爲 true
,則解析 "Host" 的 header 域,並支持 X-Forwarded-Host
app.subdomainOffset
默認爲2,表示 .subdomains
所忽略的字符偏移量。
Koa 應用並不是是一個 1-to-1 表徵關係的 HTTP 服務器。 一個或多個Koa應用能夠被掛載到一塊兒組成一個包含單一 HTTP 服務器的大型應用羣。
以下爲一個綁定3000端口的簡單 Koa 應用,其建立並返回了一個 HTTP 服務器,爲Server#listen()
傳遞指定參數(參數的詳細文檔請查看nodejs.org)。
var koa = require('koa'); var app = koa(); app.listen(3000);
app.listen(...)
其實是如下代碼的語法糖:
var http = require('http'); var koa = require('koa'); var app = koa(); http.createServer(app.callback()).listen(3000);
這意味着您能夠同時支持 HTTPS 和 HTTPS,或者在多個端口監聽同一個應用。
var http = require('http'); var koa = require('koa'); var app = koa(); http.createServer(app.callback()).listen(3000); http.createServer(app.callback()).listen(3001);
返回一個適合 http.createServer()
方法的回調函數用來處理請求。 您也可使用這個回調函數將您的app掛載在 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');
在進行cookie簽名時,只有設置 signed
爲 true
的時候,纔會使用密鑰進行加密:
this.cookies.set('name', 'tobi', { signed: true });
默認狀況下Koa會將全部錯誤信息輸出到 stderr,除非 NODE_ENV 是 "test"。爲了實現自定義錯誤處理邏輯(好比 centralized logging),您能夠添加 "error" 事件監聽器。
app.on('error', function(err){ log.error('server error', err); });
若是錯誤發生在 請求/響應 環節,而且其不可以響應客戶端時,Contenxt
實例也會被傳遞到 error
事件監聽器的回調函數裏。
app.on('error', function(err, ctx){ log.error('server error', err, ctx); });
當發生錯誤但仍可以響應客戶端時(好比沒有數據寫到socket中),Koa會返回一個500錯誤(Internal Server Error)。
不管哪一種狀況,Koa都會生成一個應用級別的錯誤信息,以便實現日誌記錄等目的。
Koa Context 將 node 的 request
和 response
對象封裝在一個單獨的對象裏面,其爲編寫 web 應用和 API 提供了不少有用的方法。
這些操做在 HTTP 服務器開發中常用,所以其被添加在上下文這一層,而不是更高層框架中,所以將迫使中間件須要從新實現這些經常使用方法。
context
在每一個 request 請求中被建立,在中間件中做爲接收器(receiver)來引用,或者經過 this
標識符來引用:
app.use(function *(){ this; // is the Context this.request; // is a koa Request this.response; // is a koa Response });
許多 context 的訪問器和方法爲了便於訪問和調用,簡單的委託給他們的 ctx.request
和ctx.response
所對應的等價方法, 好比說 ctx.type
和 ctx.length
代理了 response
對象中對應的方法,ctx.path
和 ctx.method
代理了 request
對象中對應的方法。
Context
詳細的方法和訪問器。
Node 的 request
對象。
Node 的 response
對象。
Koa 不支持 直接調用底層 res 進行響應處理。請避免使用如下 node 屬性:
res.statusCode
res.writeHead()
res.write()
res.end()
Koa 的 Request
對象。
Koa 的 Response
對象。
應用實例引用。
得到 cookie 中名爲 name
的值,options
爲可選參數:
'signed': 若是爲 true,表示請求時 cookie 須要進行簽名。
注意:Koa 使用了 Express 的 cookies 模塊,options 參數只是簡單地直接進行傳遞。
設置 cookie 中名爲 name
的值,options
爲可選參數:
signed
: 是否要作簽名
expires
: cookie 有效期時間
path
: cookie 的路徑,默認爲 /'
domain
: cookie 的域
secure
: false 表示 cookie 經過 HTTP 協議發送,true 表示 cookie 經過 HTTPS 發送。
httpOnly
: true 表示 cookie 只能經過 HTTP 協議發送
注意:Koa 使用了 Express 的 cookies 模塊,options 參數只是簡單地直接進行傳遞。
拋出包含 .status
屬性的錯誤,默認爲 500
。該方法可讓 Koa 準確的響應處理狀態。 Koa支持如下組合:
this.throw(403) this.throw('name required', 400) this.throw(400, 'name required') this.throw('something exploded')
this.throw('name required', 400)
等價於:
var err = new Error('name required'); err.status = 400; throw err;
注意:這些用戶級錯誤被標記爲 err.expose
,其意味着這些消息被準確描述爲對客戶端的響應,而並不是使用在您不想泄露失敗細節的場景中。
爲了不使用 Koa 的內置響應處理功能,您能夠直接賦值 this.repond = false;
。若是您不想讓 Koa 來幫助您處理 reponse,而是直接操做原生 res
對象,那麼請使用這種方法。
注意: 這種方式是不被 Koa 支持的。其可能會破壞 Koa 中間件和 Koa 自己的一些功能。其只做爲一種 hack 的方式,並只對那些想要在 Koa 方法和中間件中使用傳統 fn(req, res)
方法的人來講會帶來便利。
如下訪問器和別名與 Request 等價:
ctx.header
ctx.method
ctx.method=
ctx.url
ctx.url=
ctx.originalUrl
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.length=
ctx.length
ctx.type=
ctx.type
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.remove()
ctx.lastModified=
ctx.etag=
Koa Request
對象是對 node 的 request 進一步抽象和封裝,提供了平常 HTTP 服務器開發中一些有用的功能。
請求頭對象
請求方法
設置請求方法,在實現中間件時很是有用,好比 methodOverride()
。
以數字的形式返回 request 的內容長度(Content-Length),或者返回 undefined
。
得到請求url地址。
設置請求地址,用於重寫url地址時。
獲取請求原始地址。
獲取請求路徑名。
設置請求路徑名,並保留請求參數(就是url中?後面的部分)。
獲取查詢參數字符串(url中?後面的部分),不包含 ?。
設置查詢參數。
獲取查詢參數字符串,包含 ?。
設置查詢參數字符串。
獲取 host (hostname:port)。 當 app.proxy
設置爲 true 時,支持 X-Forwarded-Host
。
獲取 hostname。當 app.proxy
設置爲 true 時,支持 X-Forwarded-Host
。
獲取請求 Content-Type
,不包含像 "charset" 這樣的參數。
var ct = this.request.type; // => "image/png"
獲取請求 charset,沒有則返回 undefined
:
this.request.charset // => "utf-8"
將查詢參數字符串進行解析並以對象的形式返回,若是沒有查詢參數字字符串則返回一個空對象。
注意:該方法不支持嵌套解析。
好比 "color=blue&size=small":
{ color: 'blue', size: 'small' }
根據給定的對象設置查詢參數字符串。
注意:該方法不支持嵌套對象。
this.query = { next: '/login' };
檢查請求緩存是否 "fresh"(內容沒有發生變化)。該方法用於在 If-None-Match
/ ETag
, If-Modified-Since
和 Last-Modified
中進行緩存協調。當在 response headers 中設置一個或多個上述參數後,該方法應該被使用。
this.set('ETag', '123'); // cache is ok if (this.fresh) { this.status = 304; return; } // cache is stale // fetch new data this.body = yield db.find('something');
與 req.fresh
相反。
返回請求協議,"https" 或者 "http"。 當 app.proxy
設置爲 true 時,支持 X-Forwarded-Host
。
簡化版 this.protocol == "https"
,用來檢查請求是否經過 TLS 發送。
請求遠程地址。 當 app.proxy
設置爲 true 時,支持 X-Forwarded-Host
。
當 X-Forwarded-For
存在而且 app.proxy
有效,將會返回一個有序(從 upstream 到 downstream)ip 數組。 不然返回一個空數組。
以數組形式返回子域名。
子域名是在host中逗號分隔的主域名前面的部分。默認狀況下,應用的域名假設爲host中最後兩部分。其可經過設置 app.subdomainOffset
進行更改。
舉例來講,若是域名是 "tobi.ferrets.example.com":
若是沒有設置 app.subdomainOffset
,其 subdomains 爲 ["ferrets", "tobi"]
。 若是設置app.subdomainOffset
爲3,其 subdomains 爲 ["tobi"]
。
檢查請求所包含的 "Content-Type" 是否爲給定的 type 值。 若是沒有 request body,返回undefined
。 若是沒有 content type,或者匹配失敗,返回 false
。 不然返回匹配的 content-type。
// With Content-Type: text/html; charset=utf-8 this.is('html'); // => 'html' this.is('text/html'); // => 'text/html' this.is('text/*', 'text/html'); // => 'text/html' // When Content-Type is application/json this.is('json', 'urlencoded'); // => 'json' this.is('application/json'); // => 'application/json' this.is('html', 'application/*'); // => 'application/json' this.is('html'); // => false
好比說您但願保證只有圖片發送給指定路由:
if (this.is('image/*')) { // process } else { this.throw(415, 'images only!'); }
Koa request
對象包含 content negotiation 功能(由 accepts 和 negotiator 提供):
req.accepts(types)
req.acceptsEncodings(types)
req.acceptsCharsets(charsets)
req.acceptsLanguages(langs)
若是沒有提供 types,將會返回全部的可接受類型。
若是提供多種 types,將會返回最佳匹配類型。若是沒有匹配上,則返回 false
,您應該給客戶端返回 406 "Not Acceptable"
。
爲了防止缺乏 accept headers 而致使能夠接受任意類型,將會返回第一種類型。所以,您提供的類型順序很是重要。
檢查給定的類型 types(s)
是否可被接受,當爲 true 時返回最佳匹配,不然返回 false
。type
的值能夠是一個或者多個 mime 類型字符串。 好比 "application/json" 擴展名爲 "json",或者數組["json", "html", "text/plain"]
。
// Accept: text/html this.accepts('html'); // => "html" // Accept: text/*, application/json this.accepts('html'); // => "html" this.accepts('text/html'); // => "text/html" this.accepts('json', 'text'); // => "json" this.accepts('application/json'); // => "application/json" // Accept: text/*, application/json this.accepts('image/png'); this.accepts('png'); // => false // Accept: text/*;q=.5, application/json this.accepts(['html', 'json']); this.accepts('html', 'json'); // => "json" // No Accept header this.accepts('html', 'json'); // => "html" this.accepts('json', 'html'); // => "json"
this.accepts()
能夠被調用屢次,或者使用 switch:
switch (this.accepts('json', 'html', 'text')) { case 'json': break; case 'html': break; case 'text': break; default: this.throw(406, 'json, html, or text only'); }
檢查 encodings
是否能夠被接受,當爲 true
時返回最佳匹配,不然返回 false
。 注意:您應該在 encodings 中包含 identity
。
// Accept-Encoding: gzip this.acceptsEncodings('gzip', 'deflate', 'identity'); // => "gzip" this.acceptsEncodings(['gzip', 'deflate', 'identity']); // => "gzip"
當沒有傳遞參數時,返回包含全部可接受的 encodings 的數組:
// Accept-Encoding: gzip, deflate this.acceptsEncodings(); // => ["gzip", "deflate", "identity"]
注意:若是客戶端直接發送 identity;q=0
時,identity
encoding(表示no encoding) 能夠不被接受。雖然這是一個邊界狀況,您仍然應該處理這種狀況。
檢查 charsets
是否能夠被接受,若是爲 true
則返回最佳匹配, 不然返回 false
。
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 this.acceptsCharsets('utf-8', 'utf-7'); // => "utf-8" this.acceptsCharsets(['utf-7', 'utf-8']); // => "utf-8"
當沒有傳遞參數時, 返回包含全部可接受的 charsets 的數組:
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 this.acceptsCharsets(); // => ["utf-8", "utf-7", "iso-8859-1"]
檢查 langs
是否能夠被接受,若是爲 true
則返回最佳匹配,不然返回 false
。
// Accept-Language: en;q=0.8, es, pt this.acceptsLanguages('es', 'en'); // => "es" this.acceptsLanguages(['en', 'es']); // => "es"
當沒有傳遞參數時,返回包含全部可接受的 langs 的數組:
// Accept-Language: en;q=0.8, es, pt this.acceptsLanguages(); // => ["es", "pt", "en"]
檢查請求是否爲冪等(idempotent)。
返回請求的socket。
返回請求 header 中對應 field 的值。
Koa Response
對象是對 node 的 response 進一步抽象和封裝,提供了平常 HTTP 服務器開發中一些有用的功能。
Response header 對象。
Request socket。
獲取 response status。不一樣於 node 在默認狀況下 res.statusCode
爲200,res.status
並無賦值。
Response status 字符串。
經過 數字狀態碼或者不區分大小寫的字符串來設置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"
300 "multiple choices"
301 "moved permanently"
302 "moved temporarily"
303 "see other"
304 "not modified"
305 "use proxy"
307 "temporary 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 time-out"
409 "conflict"
410 "gone"
411 "length required"
412 "precondition failed"
413 "request entity too large"
414 "request-uri too large"
415 "unsupported media type"
416 "requested range not satisfiable"
417 "expectation failed"
418 "i'm a teapot"
422 "unprocessable entity"
423 "locked"
424 "failed dependency"
425 "unordered collection"
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 time-out"
505 "http version not supported"
506 "variant also negotiates"
507 "insufficient storage"
509 "bandwidth limit exceeded"
510 "not extended"
511 "network authentication required"
注意:不用擔憂記不住這些字符串,若是您設置錯誤,會有異常拋出,並列出該狀態碼錶來幫助您進行更正。
經過給定值設置 response Content-Length。
若是 Content-Length 做爲數值存在,或者能夠經過 res.body
來進行計算,則返回相應數值,不然返回 undefined
。
得到 response body。
設置 response body 爲以下值:
string
written
Buffer
written
Stream
piped
Object
json-stringified
null
no content response
若是 res.status
沒有賦值,Koa會自動設置爲 200
或 204
。
Content-Type 默認爲 text/html 或者 text/plain,兩種默認 charset 均爲 utf-8。 Content-Length 同時會被設置。
Content-Type 默認爲 application/octet-stream,Content-Length同時被設置。
Content-Type 默認爲 application/octet-stream。
Content-Type 默認爲 application/json。
獲取 response header 中字段值,field 不區分大小寫。
var etag = this.get('ETag');
設置 response header 字段 field
的值爲 value
。
this.set('Cache-Control', 'no-cache');
使用對象同時設置 response header 中多個字段的值。
this.set({ 'Etag': '1234', 'Last-Modified': date });
移除 response header 中字段 filed
。
獲取 response Content-Type
,不包含像 "charset" 這樣的參數。
var ct = this.type; // => "image/png"
經過 mime 類型的字符串或者文件擴展名設置 response Content-Type
this.type = 'text/plain; charset=utf-8'; this.type = 'image/png'; this.type = '.png'; this.type = 'png';
注意:當能夠根據 res.type
肯定一個合適的 charset
時,charset
會自動被賦值。 好比 res.type = 'html'
時,charset 將會默認設置爲 "utf-8"。然而當完整定義爲 res.type = 'text/html'
時,charset 不會自動設置。
執行 [302] 重定向到對應 url
。
字符串 "back" 是一個特殊參數,其提供了 Referrer 支持。當沒有Referrer時,使用 alt
或者 /
代替。
this.redirect('back'); this.redirect('back', '/index.html'); this.redirect('/login'); this.redirect('http://google.com');
若是想要修改默認的 [302] 狀態,直接在重定向以前或者以後執行便可。若是要修改 body,須要在重定向以前執行。
this.status = 301; this.redirect('/cart'); this.body = 'Redirecting to shopping cart';
設置 "attachment" 的 Content-Disposition
,用於給客戶端發送信號來提示下載。filename 爲可選參數,用於指定下載文件名。
檢查 response header 是否已經發送,用於在發生錯誤時檢查客戶端是否被通知。
若是存在 Last-Modified
,則以 Date
的形式返回。
以 UTC 格式設置 Last-Modified
。您可使用 Date
或 date 字符串來進行設置。
this.response.lastModified = new Date();
設置 包含 "
s 的 ETag。注意沒有對應的 res.etag
來獲取其值。
this.response.etag = crypto.createHash('md5').update(this.body).digest('hex');
在 header 的 field
後面 追加 val
。
至關於執行res.append('Vary', field)。
Community links to discover third-party middleware for Koa, full runnable examples, thorough guides and more! If you have questions join us in IRC! 如下列出了更多第三方提供的 koa 中間件、完整實例、全面的幫助文檔等。若是有問題,請加入咱們的 IRC!
#koajs on freenode