資料來源:
http://javascript.ruanyifeng.com/nodejs/koa.html
http://koa.bootcss.com/
以下內容為摘抄,純屬做筆記加深印象。勿噴。
使用 koa 編寫 web 應用,通過組合不同的 generator,可以免除重復繁瑣的回調函數嵌套,並極大地提升錯誤處理的效率。一個Koa應用就是一個對象,包含了一個middleware數組,這個數組由一組Generator函數組成。這些函數負責對HTTP請求進行各種加工,比如生成緩存、指定代理、請求重定向等等。這些中間件函數基於 request 請求以一個類似於棧的結構組成並依次執行。
Koa 包含了像 content-negotiation(內容協商)、cache freshness(緩存刷新)、proxy support(代理支持)和 redirection(重定向)等常用任務方法。 與提供龐大的函數支持不同,Koa只包含很小的一部分,因為Koa並不綁定任何中間件。
中間件
Koa的中間件很像Express的中間件,也是對HTTP請求進行處理的函數,但是必須是一個Generator函數。而且,Koa的中間件是一個級聯式(Cascading)的結構,也就是說,屬於是層層調用,第一個中間件調用第二個中間件,第二個調用第三個,以此類推。上游的中間件必須等到下游的中間件返回結果,才會繼續執行,這點很像遞歸。
中間件通過當前應用的use方法注冊。
app.use(function* (next){
var start = new Date; // (1)
yield next; // (2)
var ms = new Date - start; // (3)
console.log('%s %s - %s', this.method, this.url, ms); // (4)
});
Generator函數內部使用yield命令,將程序的執行權轉交給下一個中間件,即yield next,要等到下一個中間件返回結果,才會繼續往下執行。只要有一個中間件缺少yield next語句,后面的中間件都不會執行,這一點要引起注意。
如果想跳過一個中間件,可以直接在該中間件的第一行語句寫上return yield next。
app.use(function* (next) {
if (skip) return yield next; })
由於Koa要求中間件唯一的參數就是next,導致如果要傳入其他參數,必須另外寫一個返回Generator函數的函數。
function logger(format) {
return function *(next){ var str = format .replace(':method', this.method) .replace(':url', this.url); console.log(str); yield next; } } app.use(logger(':method :url'));
路由
可以通過this.path屬性,判斷用戶請求的路徑,從而起到路由作用。
app.use(function* (next) {
if (this.path === '/') { this.body = 'we are at home!'; } else { yield next; } })
復雜的路由需要安裝koa-router插件。
var app = require('koa')();
var Router = require('koa-router'); var myRouter = new Router(); myRouter.get('/', function *(next) { //router.get方法的第一個參數是根路徑,第二個參數是對應的函數方法。
this.response.body = 'Hello World!';
});
app.use(myRouter.routes());
app.listen(3000);
Koa-router實例提供一系列動詞方法,即一種HTTP動詞對應一種方法。典型的動詞方法有以下五種。
- router.get()
- router.post()
- router.put()
- router.del()
- router.patch()
這些動詞方法可以接受兩個參數,第一個是路徑模式,第二個是對應的控制器方法(中間件),定義用戶請求該路徑時服務器行為。
var router = new Router({ prefix: '/users' }); router.get('/', ...); // 等同於"/users" router.get('/:id', ...); // 等同於"/users/:id"
app.listen(...)
Koa 應用並非是一個 1-to-1 表征關系的 HTTP 服務器。 一個或多個Koa應用可以被掛載到一起組成一個包含單一 HTTP 服務器的大型應用群。
如下為一個綁定3000端口的簡單 Koa 應用,其創建並返回了一個 HTTP 服務器。
var koa = require('koa'); var app = koa(); app.listen(3000);
app.callback()
返回一個適合 http.createServer() 方法的回調函數用來處理請求。 您也可以使用這個回調函數將您的app掛載在 Connect/Express 應用上。
app.use(function)
為應用添加指定的中間件,https://github.com/koajs/koa/wiki#middleware,也就是向middleware數組添加Generator函數。
app.keys=
設置簽名Cookie密鑰,該密鑰會被傳遞給 KeyGrip。
自己生成秘鑰實例:
app.keys = ['im a newer secret', 'i like turtle']; app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
錯誤處理
默認情況下Koa會將所有錯誤信息輸出到 stderr,除非 NODE_ENV 是 "test"。為了實現自定義錯誤處理邏輯(比如 centralized logging),您可以添加 "error" 事件監聽器。
app.on('error', function(err, ctx){
log.error('server error', err, ctx);
});
Context(上下文)
Koa Context 將 node 的 request 和 response 對象封裝在一個單獨的對象里面,其為編寫 web 應用和 API 提供了很多有用的方法。
context 在每個 request 請求中被創建,在中間件中作為接收器(receiver)來引用,或者通過 this 標識符來引用:
app.use(function *(){ this; // is the Context this.request; // is a koa Request this.response; // is a koa Response });
CSRF攻擊
CSRF攻擊是指用戶的session被劫持,用來冒充用戶的攻擊。
koa-csrf插件用來防止CSRF攻擊。原理是在session之中寫入一個秘密的token,用戶每次使用POST方法提交數據的時候,必須含有這個token,否則就會拋出錯誤。
數據壓縮
koa-compress模塊可以實現數據壓縮。
app.use(require('koa-compress')())
app.use(function* () {
this.type = 'text/plain'
this.body = fs.createReadStream('filename.txt')
})
API
-
ctx.req
Node 的 request 對象。 -
ctx.res
Node 的 response 對象。 -
ctx.request
Koa 的 Request 對象。 -
ctx.response
Koa 的 Response 對象。 -
ctx.app
應用實例引用。 -
ctx.cookies.get(name, [options])
獲得 cookie 中名為 name 的值,options 為可選參數:- 'signed': 如果為 true,表示請求時 cookie 需要進行簽名。
-
ctx.cookies.set(name, value, [options])
設置 cookie 中名為 name 的值,options 為可選參數:- signed: 是否要做簽名
- expires: cookie 有效期時間
- path: cookie 的路徑,默認為 /'
- domain: cookie 的域
- secure: false 表示 cookie 通過 HTTP 協議發送,true 表示 cookie 通過 HTTPS 發送。
- httpOnly: true 表示 cookie 只能通過 HTTP 協議發送
-
ctx.throw(msg, [status])
拋出包含 .status 屬性的錯誤,默認為 500。該方法可以讓 Koa 准確的響應處理狀態。
請求(Request)API
Koa Request 對象是對 node 的 request 進一步抽象和封裝,提供了日常 HTTP 服務器開發中一些有用的功能。
-
req.header
請求頭對象 -
req.method
請求方法 -
req.method=
設置請求方法,在實現中間件時非常有用,比如 methodOverride()。 -
req.length
以數字的形式返回 request 的內容長度(Content-Length),或者返回 undefined。 -
req.url
獲得請求url地址。 -
req.url=
設置請求地址,用於重寫url地址時。 -
req.originalUrl
獲取請求原始地址。 -
req.path
獲取請求路徑名。 -
req.path=
設置請求路徑名,並保留請求參數(就是url中?后面的部分)。 -
req.querystring
獲取查詢參數字符串(url中?后面的部分),不包含 ?。 -
req.querystring=
設置查詢參數。 -
req.search
獲取查詢參數字符串,包含 ?。 -
req.search=
設置查詢參數字符串。 -
req.host
-
req.hostname
-
req.charset
-
req.query
將查詢參數字符串進行解析並以對象的形式返回,如果沒有查詢參數字字符串則返回一個空對象。 -
req.query=
根據給定的對象設置查詢參數字符串。 -
req.fresh
檢查請求緩存是否 "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.stale
與 req.fresh 相反。 -
req.protocol
返回請求協議,"https" 或者 "http"。 當 app.proxy 設置為 true 時,支持 X-Forwarded-Host。 -
req.secure
簡化版 this.protocol == "https",用來檢查請求是否通過 TLS 發送。 -
req.ip
請求遠程地址。 當 app.proxy 設置為 true 時,支持 X-Forwarded-Host。 -
req.is(type)
檢查請求所包含的 "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
-
req.accepts(types)
檢查給定的類型 types(s) 是否可被接受,當為 true 時返回最佳匹配,否則返回 false。type 的值可以是一個或者多個 mime 類型字符串。 -
req.acceptsEncodings(encodings)
檢查 encodings 是否可以被接受,當為 true 時返回最佳匹配,否則返回 false。 注意:您應該在 encodings 中包含 identity。
// Accept-Encoding: gzip this.acceptsEncodings('gzip', 'deflate', 'identity'); // => "gzip" this.acceptsEncodings(['gzip', 'deflate', 'identity']); // => "gzip"
- req.acceptsCharsets(charsets)
檢查 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"
-
req.socket
返回請求的socket。 -
req.get(field)
返回請求 header 中對應 field 的值。
響應(Response)API
Koa Response 對象是對 node 的 response 進一步抽象和封裝,提供了日常 HTTP 服務器開發中一些有用的功能。
-
res.header
Response header 對象。 -
res.socket
Response socket。 -
res.status
獲取 response status。不同於 node 在默認情況下 res.statusCode 為200,res.status 並沒有賦值。 -
res.statusString
Response status 字符串。 -
res.status=
通過數字狀態碼或者不區分大小寫的字符串來設置response status. -
res.length=
通過給定值設置 response Content-Length。 -
res.length
如果 Content-Length 作為數值存在,或者可以通過 res.body 來進行計算,則返回相應數值,否則返回 undefined。 -
res.body
獲得 response body。 -
res.body=
-
res.get(field)
獲取 response header 中字段值,field 不區分大小寫。
var etag = this.get('ETag');
- res.set(field, value)
設置 response header 字段 field 的值為 value。
this.set('Cache-Control', 'no-cache');
- res.set(fields)
使用對象同時設置 response header 中多個字段的值。
this.set({ 'Etag': '1234', 'Last-Modified': date });
-
res.remove(field)
移除 response header 中字段 filed。 -
res.type
獲取 response Content-Type,不包含像 "charset" 這樣的參數。 -
res.type=
通過 mime 類型的字符串或者文件擴展名設置 response Content-Type. -
res.redirect(url, [alt])
執行 [302] 重定向到對應 url。 -
res.lastModified
如果存在 Last-Modified,則以 Date 的形式返回。 -
res.lastModified=
以 UTC 格式設置 Last-Modified。您可以使用 Date 或 date 字符串來進行設置。 -
res.append(field, val)
在 header 的 field 后面 追加 val。 -
res.vary(field)
相當於執行res.append('Vary', field)。
