第四代Express框架koa簡介 簡介 koa和express koa使用介紹

簡介

熟悉Spring MVC的朋友應該都清楚Spring MVC是基於servlet的代碼框架,這是最傳統的web框架。然後在Spring5中引入了Spring WebFlux,這是基於reactive-netty的異步IO框架。

同樣的,nodejs在最初的Express 3基礎上發展起來了異步的koa框架。koa使用了promises和aysnc來避免JS中的回調地獄,並且簡化了錯誤處理。

今天我們要來介紹一下這個優秀的nodejs框架koa。

koa和express

koa不再使用nodejs的req和res,而是封裝了自己的ctx.request和ctx.response。

express可以看做是nodejs的一個應用框架,而koa則可以看成是nodejs 的http模塊的抽象。

和express提供了Middleware,Routing,Templating,Sending Files和JSONP等特性不同的是,koa的功能很單一,如果你想使用其他的一些功能比如routing,sending files等功能,可以使用koa的第三方中間件。

koa並不是來替換express的,就像spring webFlux並不是用來替換spring MVC的。koa只是用Promises改寫了控制流,並且避免了回調地獄,並提供了更好的異常處理機制。

koa使用介紹

koa需要node v7.6.0+版本來支持ES2015和async function。

我們看一個最最簡單的koa應用:

const Koa = require('koa');
const app = module.exports = new Koa();

app.use(async function(ctx) {
  ctx.body = 'Hello World';
});

if (!module.parent) app.listen(3000);

koa應用程序就是一個包含了很多箇中間件的對象,這些中間件將會按照類似stack的執行順序一個相應request。

中間件的級聯關係

koa.use中傳入的是一個function,我們也可以稱之爲中間件。

koa可以use很多箇中間件,舉個例子:

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

app.use(async (ctx, next) => {
  await next();
  console.log('log3');
});

app.use(async (ctx, next) => {
  await next();
  console.log('log2');
});

app.use(async ctx => {
  console.log('log3');
});

app.listen(3000);

上面的例子中,我們調用了多次next,只要我們調用next,調用鏈就會傳遞到下一個中間件進行處理,一直到某個中間件不再調用next
爲止。

上面的代碼運行輸出:

log1
log2
log3

koa的構造函數

我們看下koa的構造函數:

constructor(options) {
    super();
    options = options || {};
    this.proxy = options.proxy || false;
    this.subdomainOffset = options.subdomainOffset || 2;
    this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For';
    this.maxIpsCount = options.maxIpsCount || 0;
    this.env = options.env || process.env.NODE_ENV || 'development';
    if (options.keys) this.keys = options.keys;
    this.middleware = [];
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
    // util.inspect.custom support for node 6+
    /* istanbul ignore else */
    if (util.inspect.custom) {
      this[util.inspect.custom] = this.inspect;
    }
  }

可以看到koa接收下面幾個參數:

  • app.env 默認值是NODE_ENV或者development
  • app.keys 爲cookie簽名的keys

看下怎麼使用:

app.keys = ['secret1', 'secret2'];
app.keys = new KeyGrip(['secret1', 'secret2'], 'sha256');

ctx.cookies.set('name', 'jack', { signed: true });
  • app.proxy 是否支持代理
  • app.subdomainOffset 表示子域名是從第幾級開始的,這個參數決定了request.subdomains的返回結果,默認值爲2
  • app.proxyIpHeader proxy ip header默認值是X-Forwarded-For
  • app.maxIpsCount 從proxy ip header讀取的最大ip個數,默認值是0表示無限制。

我們可以這樣用:

const Koa = require('koa');
const app = new Koa({ proxy: true });

或者這樣用:

const Koa = require('koa');
const app = new Koa();
app.proxy = true;

啓動http server

koa是一種web框架,web框架就需要開啓http服務,要啓動http服務,需要調用nodejs中的Server#listen()方法。

在koa中,我們可以很方便的使用koa#listen方法來啓動這個http server:

const Koa = require('koa');
const app = new Koa();
app.listen(3000);

上面的代碼相當於:

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);

自定義中間件

koa中的中間件是參數值爲(ctx, next)的function。在這些方法中,需要手動調用next()以傳遞到下一個middleware。

下面看一下自定義的中間件:

async function responseTime(ctx, next) {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
}

app.use(responseTime);
  • 給中間件起個名字:

雖然中間件function只接收參數(ctx, next),但是我可以將其用一個wrapper方法包裝起來,在wrapper方法中,我們給中間件起個名字 :

function logger(name) {
  return async function logger(ctx, next) {
      console.log(name);
      await next();
  };    
}
  • 自定義中間件的擴展:

上面的wrapper創建方式還有另外一個好處,就是可以在自定義中間件中訪問傳入的參數,從而可以根據傳入的參數,對自定義中間件進行擴展。

function logger(format) {
  format = format || ':method ":url"';

  return async function (ctx, next) {
    const str = format
      .replace(':method', ctx.method)
      .replace(':url', ctx.url);

    console.log(str);

    await next();
  };
}

app.use(logger());
app.use(logger(':method :url'));
  • 組合多箇中間件:

當有多箇中間件的情況下,我們可以使用compose將其合併:

const compose = require('koa-compose');
const Koa = require('koa');
const app = module.exports = new Koa();

// x-response-time

async function responseTime(ctx, next) {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  ctx.set('X-Response-Time', ms + 'ms');
}

// logger

async function logger(ctx, next) {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  if ('test' != process.env.NODE_ENV) {
    console.log('%s %s - %s', ctx.method, ctx.url, ms);
  }
}

// response

async function respond(ctx, next) {
  await next();
  if ('/' != ctx.url) return;
  ctx.body = 'Hello World';
}

// composed middleware

const all = compose([
  responseTime,
  logger,
  respond
]);

app.use(all);

if (!module.parent) app.listen(3000);

異常處理

在koa中怎麼進行異常處理呢?

通用的方法就是try catch:


app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    err.status = err.statusCode || err.status || 500;
    throw err;
  }
});

當然你也可以自定義默認的error處理器:

app.on('error', err => {
  log.error('server error', err)
});

我們還可以傳入上下文信息:

app.on('error', (err, ctx) => {
  log.error('server error', err, ctx)
});

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/koa-startup/

本文來源:flydean的博客

歡迎關注我的公衆號:「程序那些事」最通俗的解讀,最深刻的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章