摘要:應用級別頂層處理在上面中間件執(zhí)行時看到,會自動幫我們捕獲錯誤并處理,如下捕獲錯誤在中處理我們看發(fā)現(xiàn)它事實上是出發(fā)監(jiān)聽的事件假如我們沒有定義回調(diào)怎么辦呢,也為我們定義了默認的錯誤處理函數(shù)方法做了判斷全文完
koa原理淺析
選取的版本為koa2原文鏈接
koa的源碼由四個文件組成
application.js koa的骨架 context.js ctx的原型 request.js request的原型 response.js response的原型基本用法
const Koa = require("koa"); const app = new Koa(); app.use(async ctx => { ctx.body = "Hello World"; }); app.listen(3000);初始服務器
利用http模塊創(chuàng)建服務器
const app = http.createServer((req, res) => { ... }) app.listen(3000)
事實上koa把這些包在了其listen方法中
listen(...args) { debug("listen"); const server = http.createServer(this.callback()); return server.listen(...args); }
顯然this.callback()返回的是一個形如下面的函數(shù)
(req, res) => {}上下文ctx
callback方法如下
callback() { const fn = compose(this.middleware); if (!this.listeners("error").length) this.on("error", this.onerror); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; }
ctx在koa中事實上是一個包裝了request和response的對象,從createContext中可以看到起繼承自context
createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.cookies = new Cookies(req, res, { keys: this.keys, secure: request.secure }); request.ip = request.ips[0] || req.socket.remoteAddress || ""; context.accept = request.accept = accepts(req); context.state = {}; return context; }
可以看到ctx.request繼承自request,ctx.response繼承自response,查看response和request可以看到里面大都是set和get方法(獲取query,設置header)等等。并且ctx代理了ctx.request和ctx.response的方法,在源碼中可以看到
delegate(proto, "response") .method("attachment") .method("redirect") .method("remove") .method("vary") .method("set") .method("append") .method("flushHeaders") .access("status") .access("message") .access("body") .access("length") .access("type") .access("lastModified") .access("etag") .getter("headerSent") .getter("writable"); /** * Request delegation. */ delegate(proto, "request") .method("acceptsLanguages") .method("acceptsEncodings") .method("acceptsCharsets") .method("accepts") .method("get") .method("is") .access("querystring") .access("idempotent") .access("socket") .access("search") .access("method") .access("query") .access("path") .access("url") .getter("origin") .getter("href") .getter("subdomains") .getter("protocol") .getter("host") .getter("hostname") .getter("URL") .getter("header") .getter("headers") .getter("secure") .getter("stale") .getter("fresh") .getter("ips") .getter("ip");
所以我們可以直接這么寫
ctx.url 等價于 ctx.request.url中間件
我們再看一下callback函數(shù),觀察發(fā)現(xiàn)compose模塊十分的神奇,我暫且把它稱為是一個迭代器,它實現(xiàn)了中間件的順序執(zhí)行
const fn = compose(this.middleware); 打印fn如下 function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error("next() called multiple times")) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, function next () { return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } }
最初接觸koa的時候我疑惑為什么我寫了
ctx.body = "hello world"
并沒有ctx.response.end()之類的方法,事實上koa已經(jīng)幫我們做了處理,在handleRequest方法中
const handleResponse = () => respond(ctx); // fnMiddleware即為上面compose之后的fn fnMiddleware(ctx).then(handleResponse).catch(onerror)
fnMiddleware返回的是一個promise,在中間件邏輯完成后在respond函數(shù)中最終去處理ctx.body
function respond(ctx) { // allow bypassing koa if (false === ctx.respond) return; const res = ctx.res; if (!ctx.writable) return; let body = ctx.body; const code = ctx.status; // ignore body if (statuses.empty[code]) { // strip headers ctx.body = null; return res.end(); } if ("HEAD" == ctx.method) { if (!res.headersSent && isJSON(body)) { ctx.length = Buffer.byteLength(JSON.stringify(body)); } return res.end(); } // status body if (null == body) { body = ctx.message || String(code); if (!res.headersSent) { ctx.type = "text"; ctx.length = Buffer.byteLength(body); } return res.end(body); } // responses if (Buffer.isBuffer(body)) return res.end(body); if ("string" == typeof body) return res.end(body); if (body instanceof Stream) return body.pipe(res); // body: json body = JSON.stringify(body); if (!res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); }錯誤處理
(非首部)中間件層處理(我瞎起的)
對于每個中間件可能發(fā)生的錯誤,可以直接在該中間件捕獲
app.use((ctx, next) => { try { ... } catch(err) { ... } })
(首部)中間件層處理
事實上,我們只要在第一個中間件添加try... catch... ,整個中間件組的錯誤都是可以捕獲的到的。
(應用級別)頂層處理
app.on("error", (err) = {})
在上面中間件執(zhí)行時看到,koa會自動幫我們捕獲錯誤并處理,如下
try { return Promise.resolve(fn(context, function next () { return dispatch(i + 1) })) } catch (err) { // 捕獲錯誤 return Promise.reject(err) } // 在ctx.onerror中處理 const onerror = err => ctx.onerror(err); fnMiddleware(ctx).then(handleResponse).catch(onerror)
我們看ctx.onerror發(fā)現(xiàn)它事實上是出發(fā)app監(jiān)聽的error事件
onerror(err) { // delegate this.app.emit("error", err, this);
假如我們沒有定義error回調(diào)怎么辦呢,koa也為我們定義了默認的錯誤處理函數(shù)
callback方法做了判斷
callback() { ... if (!this.listeners("error").length) this.on("error", this.onerror); ... }
全文完
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92098.html
摘要:啟動流程主要的啟動流程就是下面的步引入包實例化編寫中間件監(jiān)聽服務器引入包引入包其實就是引入的一個繼承于原生的類的類其中就包含了等原型方法實例化執(zhí)行,將等對象封裝在實例中編寫中間件首先判斷的類型,不是方法直接拋錯是生成器函數(shù)的話用封裝是函數(shù) 啟動流程 koa 主要的啟動流程就是下面的 4 步:引入 koa 包 => 實例化 koa => 編寫中間件 => 監(jiān)聽服務器 const koa ...
摘要:任何一層報錯,都能用捕獲總結是一個非常輕量級的框架,只實現(xiàn)了中間件處理流程和對對象的封裝。其他的功能都由外部中間件提供。 koa 的中間件機制巧妙的運用了閉包和 async await 的特點,形成了一個洋蔥式的流程,和 JS 的事件流 (捕獲 -> target -> 冒泡) 相似 handleRequest(ctx, fnMiddleware) { const res ...
摘要:代碼結構執(zhí)行流程上面兩張圖主要將的整體代碼結構和大概的執(zhí)行流程畫了出來,畫的不夠具體。那下面主要講中的幾處的關鍵代碼解讀一下。全局的路由參數(shù)處理的中間件組成的對象。 代碼結構 showImg(https://segmentfault.com/img/remote/1460000007468236?w=1425&h=1772); 執(zhí)行流程 showImg(https://segmentf...
摘要:前言被認為是第二代,它最大的特點就是獨特的中間件流程控制,是一個典型的洋蔥模型。這段代碼就很巧妙的實現(xiàn)了兩點將一路傳下去給中間件將中的下一個中間件作為未來的返回值這兩點也是洋蔥模型實現(xiàn)的核心。 前言 koa被認為是第二代node web framework,它最大的特點就是獨特的中間件流程控制,是一個典型的洋蔥模型。koa和koa2中間件的思路是一樣的,但是實現(xiàn)方式有所區(qū)別,koa2在...
摘要:當運行到時,不會暫停,而是直接跳進函數(shù)執(zhí)行函數(shù)內(nèi)的代碼。由于函數(shù)中沒有,因此會一直執(zhí)行完函數(shù)中的代碼,并返回至函數(shù)中執(zhí)行后面的代碼。 本系列旨在通過對co,koa等庫源碼的研究,進而理解generator在異步編程中的重大作用(ps:所有代碼請在node --harmony或者iojs環(huán)境中運行) koa中間件的形式 相信用過koa的小伙伴一定很熟悉下面這段代碼 var app ...
閱讀 1887·2021-11-11 16:55
閱讀 2088·2021-10-08 10:13
閱讀 750·2019-08-30 11:01
閱讀 2159·2019-08-29 13:19
閱讀 3285·2019-08-28 18:18
閱讀 2625·2019-08-26 13:26
閱讀 584·2019-08-26 11:40
閱讀 1876·2019-08-23 17:17