# 應用
Koa 應用是一個包含一系列中間件 generator 函數的對象。 這些中間件函數基于 request 請求以一個類似于棧的結構組成并依次執行。 Koa 類似于其他中間件系統(比如 Ruby's Rack 、Connect 等), 然而 Koa 的核心設計思路是為中間件層提供高級語法糖封裝,以增強其互用性和健壯性,并使得編寫中間件變得相當有趣。
Koa 包含了像 content-negotiation(內容協商)、cache freshness(緩存刷新)、proxy support(代理支持)和 redirection(重定向)等常用任務方法。 與提供龐大的函數支持不同,Koa只包含很小的一部分,因為Koa并不綁定任何中間件。
任何教程都是從 hello world 開始的,Koa也不例外^_^
```
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')。
下面的例子在頁面中返回 "Hello World",然而當請求開始時,請求先經過 `x-response-time` 和 `logging` 中間件,并記錄中間件執行起始時間。 然后將控制權交給 `reponse` 中間件。當中間件運行到 `yield next` 時,函數掛起并將控制前交給下一個中間件。當沒有中間件執行 `yield next` 時,程序棧會逆序喚起被掛起的中間件來執行接下來的代碼。
```
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` 實例屬性,目前支持的配置項如下:
* `app.name` 應用名稱(可選項)
* `app.env` 默認為 **NODE_ENV** 或者 `development`
* `app.proxy` 如果為 `true`,則解析 "Host" 的 header 域,并支持 `X-Forwarded-Host`
* `app.subdomainOffset` 默認為2,表示 `.subdomains` 所忽略的字符偏移量。
## app.listen(...)
Koa 應用并非是一個 1-to-1 表征關系的 HTTP 服務器。 一個或多個Koa應用可以被掛載到一起組成一個包含單一 HTTP 服務器的大型應用群。
如下為一個綁定3000端口的簡單 Koa 應用,其創建并返回了一個 HTTP 服務器,為 `Server#listen()` 傳遞指定參數(參數的詳細文檔請查看[nodejs.org](http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback))。
```
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);
```
## app.callback()
返回一個適合 `http.createServer()` 方法的回調函數用來處理請求。 您也可以使用這個回調函數將您的app掛載在 Connect/Express 應用上。
## app.use(function)
為應用添加指定的中間件,詳情請看 [Middleware](https://github.com/koajs/koa/wiki#middleware)
## app.keys=
設置簽名Cookie密鑰,該密鑰會被傳遞給 [KeyGrip](https://github.com/jed/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都會生成一個應用級別的錯誤信息,以便實現日志記錄等目的。