熟悉Spring MVC的朋友應該都清楚Spring MVC是基於servlet的代碼框架,這是最傳統的web框架。而後在Spring5中引入了Spring WebFlux,這是基於reactive-netty的異步IO框架。node
一樣的,nodejs在最初的Express 3基礎上發展起來了異步的koa框架。koa使用了promises和aysnc來避免JS中的回調地獄,而且簡化了錯誤處理。react
今天咱們要來介紹一下這個優秀的nodejs框架koa。web
koa再也不使用nodejs的req和res,而是封裝了本身的ctx.request和ctx.response。spring
express能夠看作是nodejs的一個應用框架,而koa則能夠當作是nodejs 的http模塊的抽象。express
和express提供了Middleware,Routing,Templating,Sending Files和JSONP等特性不一樣的是,koa的功能很單一,若是你想使用其餘的一些功能好比routing,sending files等功能,可使用koa的第三方中間件。promise
koa並非來替換express的,就像spring webFlux並非用來替換spring MVC的。koa只是用Promises改寫了控制流,而且避免了回調地獄,並提供了更好的異常處理機制。cookie
koa須要node v7.6.0+版原本支持ES2015和async function。app
咱們看一個最最簡單的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。dom
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的構造函數:
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.keys = ['secret1', 'secret2']; app.keys = new KeyGrip(['secret1', 'secret2'], 'sha256'); ctx.cookies.set('name', 'jack', { signed: true });
咱們能夠這樣用:
const Koa = require('koa'); const app = new Koa({ proxy: true });
或者這樣用:
const Koa = require('koa'); const app = new Koa(); app.proxy = true;
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的博客
歡迎關注個人公衆號:「程序那些事」最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!