koa服務器入門

介紹html

koa是一個相對於express來講,更小,更健壯,更富表現力的Web框架。koa經過組合不一樣的generator來避免繁瑣的回調函數調用。koa的核心庫沒有綁定任何的中間件,僅僅提供了一個輕量優雅的函數庫,使得編寫Web應用變得駕輕就熟。git

使用github

在項目目錄路徑下運行命令 npm install --save-dev koa 就能夠在本地安裝koa模塊。express

安裝完成後,寫一個hello world來驗證是否生效。npm

hello worldjson

hello world代碼十分簡單。數組

const koa = require('koa');
const app = koa();session

app.use(function *() {
this.body = 'hello world';
});app

app.listen(3000);
上面8行代碼就實現了koa的hello world。第4行調用的 app.use() 傳入了一個generator方法,就是koa中間件的基本實現。koa應用的實現就是由一個一個的中間件來實現。每個中間件都是一個generator方法,經過yield語句,將一個一箇中間件邏輯級聯起來。舉個例子:框架

const koa = require('koa');
const app = koa();

// x-response-time
app.use(function *(next) {
console.log('line 1');
const start = new Date;
yield next;
console.log('line 5');
const ms = new Date - start;
this.set('X-Response-Time', ${ms}ms);
});

// log time
app.use(function *(next) {
console.log('line 2');
const start = new Date;
yield next;
console.log('line4');
const ms = new Date - start;
console.info('%s %s : %s ms', this.method, this.url, ms);
});

// response
app.use(function *(next) {
console.log('line3');
this.body = 'hello world';
});

app.listen(3000);
運行上面的代碼後,當有一個請求被觸發時,控制檯的log就會打印以下日誌(請忽略上面代碼中的info日誌):

line 1
line 2
line 3
line 4
line 5
能夠看出,koa中間件的運行,被yield分割成了兩段來運行,運行的時間順序以下圖:

koa框架自己的功能十分簡單,koa應用的功能都是經過中間件來實現的,下面咱們來介紹經常使用的幾個koa中間件。

koa-static

koa-static是管理靜態文件請求的中間件。好比要請求html,JS,圖片的靜態文件時,就可使用koa-static來實現。

舉個例子,好比項目根目錄下得static目錄用於存放靜態文件,那麼以下代碼就能夠實現該目錄的靜態文件請求

const path = require('path');
const staticServer = require('koa-static');

app.use(staticServer(path.join(__dirname, 'static')));
koa-router

koa-router是一個路由中間件,用法以下:

const router = require('koa-router')();

// 監聽url請求
router.get('/list', function *() {
// ...
});
router.post('/user/register', function *() {
// ...
});
koa-safe-jsonp

JSONP格式返回的中間件,用法:

const jsonp = require('koa-safe-jsonp');
const router = require('koa-router');
const koa = require('koa');

const app = koa();
jsonp(app);

router.get('./list', function *() {
const list = [];
this.json = list;
});
koa-session

session管理的中間件,用法:

const session = require('koa-session');

app.use(session(app));

router.post('./user/login', function *() {
this.session.user = user;
});
koa-onerror

koa-onerror用於格式化異常狀況的頁面輸出。用法:

const onerror = require('koa-onerror');
const koa = require('koa');

const app = koa();
onerror(app);
中間件實現

上面列出的經常使用中間件都是十分經常使用的中間件,可是在業務開發過程當中,咱們會根據業務場景實現一些專門的中間件,那麼如何開發一箇中間件呢?

如今咱們來說下如何實現koa的中間件。好比咱們要實現一個統計請求響應實現的功能,就能夠開發一個統計請求響應時間的中間件來使用。

基本從上面的例子中,能夠看出,中間件都是經過 app.use() 來註冊的,而 app.use() 方法的參數是一個generator方法。因此, koa中間件須要返回一個generator方法,因此實現代碼以下:

// middleware/timer.js
module.exports = function() {
return function *(next) {

const path = this.path;
const start = new Date;
yield next;
const end = new Date;
console.log(`${path} response time: ${ end - start }ms`);

}
};

// server.js
const timer = require('./middleware/timer.js');
app.use(timer());
上面這段代碼,就實現了統計請求響應時間的功能。功能十分簡單, middleware/timer.js 中直接返回了一個generator方法,在裏面分別統計請求開始時間和結束時間,並打印時間差。

咱們能夠將中間件設計的稍微複雜一點,好比咱們能夠控制過濾掉一部分請求不打印,打印格式也能夠進行控制。這個時候咱們須要傳入一些參數來控制中間件的功能,代碼以下:

// middleware/timer.js
module.exports = function timer(options) {
return function *timer() {

let start = new Date;
const path = this.path;
const method = this.request.method;
if ( method !== options.filter.method ) start = 0;
yield next;
const end = new Date;
if(start !== 0 && end - start > options.filter.min) {
  console.log(options.format.replace(/:url/g, path).replace(/:time/g, `${ end - start }ms`));
}

}
};

// server.js
const timer = require('./middleware/timer.js');
// GET 請求超過100ms時,打印日誌,日誌格式: url time
app.use(timer({
format: ':url :time'
filter: {

min: 100
method: 'GET'

}
}));
koa源碼理解

最前面介紹的時候有說到,koa的核心庫十分簡單,沒有綁定任何中間件。因此,在最後說一下koa的源碼。

koa.js的源碼能夠去 https://github.com/koajs/koa 得到。

koa.js的源碼有4個文件,分別是 lib/application.js , lib/context.js , lib/request.js , lib/response.js 。從名字中能夠看出來, context.js , request.js , response.js 分別是上下文環境對象,request對象,response對象,而 application.js 就是koa.js的核心代碼。

在上面的例子中,都使用了兩個接口, use 和 listen 。下面咱們先介紹下這兩個方法, 下面是 application.js 中這兩個方法的源碼:

// ...
app.listen = function(){
debug('listen');
var server = http.createServer(this.callback());
return server.listen.apply(server, arguments);
};
// ...
app.use = function(fn){
if (!this.experimental) {

// es7 async functions are not allowed,
// so we have to make sure that `fn` is a generator function
assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function');

}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
};
// ...
能夠看出來, use 的做用就是將傳入的中間件generator方法放到 this.middleware 中。 listen 接口的做用其實就是啓動了一個server,並將請求處理設置爲 this.callback() 的返回方法。

而後咱們來看下 this.callback 怎麼寫的:

// ...
app.callback = function(){
if (this.experimental) {

console.error('Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed.')

}
var fn = this.experimental

? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));

var self = this;

if (!this.listeners('error').length) this.on('error', this.onerror);

return function(req, res){

res.statusCode = 404;
var ctx = self.createContext(req, res);
onFinished(res, ctx.onerror);
fn.call(ctx).then(function () {
  respond.call(ctx);
}).catch(ctx.onerror);

}
};
// ...
其中的核心代碼是這兩句:

// ...
co.wrap(compose(this.middleware));
// ...
return function(req, res){
// ...
fn.call(ctx).then(function () {

respond.call(ctx);

}).catch(ctx.onerror);}// ...其中 compose(this.middleware) 的做用是將傳入的中間件數組合併成層層調用的generator函數。 co.wrap() 方法的做用是將generator數轉化成一個自執行的函數。最後的 fn.call(ctx) 就開始逐步執行中間件了。

相關文章
相關標籤/搜索