Egg 異常處理

2020-02-06 14:11 更新

異常捕獲

得益于框架支持的異步編程模型,錯(cuò)誤完全可以用 try catch 來(lái)捕獲。在編寫應(yīng)用代碼時(shí),所有地方都可以直接用 try catch 來(lái)捕獲異常。

// app/service/test.js
try {
const res = await this.ctx.curl('http://eggjs.com/api/echo', { dataType: 'json' });
if (res.status !== 200) throw new Error('response status is not 200');
return res.data;
} catch (err) {
this.logger.error(err);
return {};
}

按照正常代碼寫法,所有的異常都可以用這個(gè)方式進(jìn)行捕獲并處理,但是一定要注意一些特殊的寫法可能帶來(lái)的問題。打一個(gè)不太正式的比方,我們的代碼全部都在一個(gè)異步調(diào)用鏈上,所有的異步操作都通過 await 串接起來(lái)了,但是只要有一個(gè)地方跳出了異步調(diào)用鏈,異常就捕獲不到了。

// app/controller/home.js
class HomeController extends Controller {
async buy () {
const request = {};
const config = await ctx.service.trade.buy(request);
// 下單后需要進(jìn)行一次核對(duì),且不阻塞當(dāng)前請(qǐng)求
setImmediate(() => {
ctx.service.trade.check(request).catch(err => ctx.logger.error(err));
});
}
}

在這個(gè)場(chǎng)景中,如果 service.trade.check 方法中代碼有問題,導(dǎo)致執(zhí)行時(shí)拋出了異常,盡管框架會(huì)在最外層通過 try catch 統(tǒng)一捕獲錯(cuò)誤,但是由于 setImmediate 中的代碼『跳出』了異步鏈,它里面的錯(cuò)誤就無(wú)法被捕捉到了。因此在編寫類似代碼的時(shí)候一定要注意。

當(dāng)然,框架也考慮到了這類場(chǎng)景,提供了 ctx.runInBackground(scope) 輔助方法,通過它又包裝了一個(gè)異步鏈,所有在這個(gè) scope 里面的錯(cuò)誤都會(huì)統(tǒng)一捕獲。

class HomeController extends Controller {
async buy () {
const request = {};
const config = await ctx.service.trade.buy(request);
// 下單后需要進(jìn)行一次核對(duì),且不阻塞當(dāng)前請(qǐng)求
ctx.runInBackground(async () => {
// 這里面的異常都會(huì)統(tǒng)統(tǒng)被 Backgroud 捕獲掉,并打印錯(cuò)誤日志
await ctx.service.trade.check(request);
});
}
}

為了保證異常可追蹤,必須保證所有拋出的異常都是 Error 類型,因?yàn)橹挥?Error 類型才會(huì)帶上堆棧信息,定位到問題。

框架層統(tǒng)一異常處理

框架通過 onerror 插件提供了統(tǒng)一的錯(cuò)誤處理機(jī)制。對(duì)一個(gè)請(qǐng)求的所有處理方法(Middleware、Controller、Service)中拋出的任何異常都會(huì)被它捕獲,并自動(dòng)根據(jù)請(qǐng)求想要獲取的類型返回不同類型的錯(cuò)誤(基于 Content Negotiation)。

請(qǐng)求需求的格式環(huán)境errorPageUrl 是否配置返回內(nèi)容
HTML & TEXTlocal & unittest-onerror 自帶的錯(cuò)誤頁(yè)面,展示詳細(xì)的錯(cuò)誤信息
HTML & TEXT其他重定向到 errorPageUrl
HTML & TEXT其他onerror 自帶的沒有錯(cuò)誤信息的簡(jiǎn)單錯(cuò)誤頁(yè)(不推薦)
JSON & JSONPlocal & unittest-JSON 對(duì)象或?qū)?yīng)的 JSONP 格式響應(yīng),帶詳細(xì)的錯(cuò)誤信息
JSON & JSONP其他-JSON 對(duì)象或?qū)?yīng)的 JSONP 格式響應(yīng),不帶詳細(xì)的錯(cuò)誤信息

errorPageUrl

onerror 插件的配置中支持 errorPageUrl 屬性,當(dāng)配置了 errorPageUrl 時(shí),一旦用戶請(qǐng)求線上應(yīng)用的 HTML 頁(yè)面異常,就會(huì)重定向到這個(gè)地址。

在 config/config.default.js 中

// config/config.default.js
module.exports = {
onerror: {
// 線上頁(yè)面發(fā)生異常時(shí),重定向到這個(gè)頁(yè)面上
errorPageUrl: '/50x.html',
},
};

自定義統(tǒng)一異常處理

盡管框架提供了默認(rèn)的統(tǒng)一異常處理機(jī)制,但是應(yīng)用開發(fā)中經(jīng)常需要對(duì)異常時(shí)的響應(yīng)做自定義,特別是在做一些接口開發(fā)的時(shí)候??蚣茏詭У?onerror 插件支持自定義配置錯(cuò)誤處理方法,可以覆蓋默認(rèn)的錯(cuò)誤處理方法。

// config/config.default.js
module.exports = {
onerror: {
all(err, ctx) {
// 在此處定義針對(duì)所有響應(yīng)類型的錯(cuò)誤處理方法
// 注意,定義了 config.all 之后,其他錯(cuò)誤處理方法不會(huì)再生效
ctx.body = 'error';
ctx.status = 500;
},
html(err, ctx) {
// html hander
ctx.body = '<h3>error</h3>';
ctx.status = 500;
},
json(err, ctx) {
// json hander
ctx.body = { message: 'error' };
ctx.status = 500;
},
jsonp(err, ctx) {
// 一般來(lái)說,不需要特殊針對(duì) jsonp 進(jìn)行錯(cuò)誤定義,jsonp 的錯(cuò)誤處理會(huì)自動(dòng)調(diào)用 json 錯(cuò)誤處理,并包裝成 jsonp 的響應(yīng)格式
},
},
};

404

框架并不會(huì)將服務(wù)端返回的 404 狀態(tài)當(dāng)做異常來(lái)處理,但是框架提供了當(dāng)響應(yīng)為 404 且沒有返回 body 時(shí)的默認(rèn)響應(yīng)。

  • 當(dāng)請(qǐng)求被框架判定為需要 JSON 格式的響應(yīng)時(shí),會(huì)返回一段 JSON:{ "message": "Not Found" }
  • 當(dāng)請(qǐng)求被框架判定為需要 HTML 格式的響應(yīng)時(shí),會(huì)返回一段 HTML:<h1>404 Not Found</h1>

框架支持通過配置,將默認(rèn)的 HTML 請(qǐng)求的 404 響應(yīng)重定向到指定的頁(yè)面。

// config/config.default.js
module.exports = {
notfound: {
pageUrl: '/404.html',
},
};

自定義 404 響應(yīng)

在一些場(chǎng)景下,我們需要自定義服務(wù)器 404 時(shí)的響應(yīng),和自定義異常處理一樣,我們也只需要加入一個(gè)中間件即可對(duì) 404 做統(tǒng)一處理:

// app/middleware/notfound_handler.js
module.exports = () => {
return async function notFoundHandler(ctx, next) {
await next();
if (ctx.status === 404 && !ctx.body) {
if (ctx.acceptJSON) {
ctx.body = { error: 'Not Found' };
} else {
ctx.body = '<h1>Page Not Found</h1>';
}
}
};
};

在配置中引入中間件:

// config/config.default.js
module.exports = {
middleware: [ 'notfoundHandler' ],
};


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)