Egg 異常處理

2020-02-06 14:11 更新

異常捕獲

得益于框架支持的異步編程模型,錯誤完全可以用 try catch 來捕獲。在編寫應用代碼時,所有地方都可以直接用 try catch 來捕獲異常。

// 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 {};
}

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

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

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

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

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

為了保證異??勺粉?,必須保證所有拋出的異常都是 Error 類型,因為只有 Error 類型才會帶上堆棧信息,定位到問題。

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

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

請求需求的格式環(huán)境errorPageUrl 是否配置返回內容
HTML & TEXTlocal & unittest-onerror 自帶的錯誤頁面,展示詳細的錯誤信息
HTML & TEXT其他重定向到 errorPageUrl
HTML & TEXT其他onerror 自帶的沒有錯誤信息的簡單錯誤頁(不推薦)
JSON & JSONPlocal & unittest-JSON 對象或對應的 JSONP 格式響應,帶詳細的錯誤信息
JSON & JSONP其他-JSON 對象或對應的 JSONP 格式響應,不帶詳細的錯誤信息

errorPageUrl

onerror 插件的配置中支持 errorPageUrl 屬性,當配置了 errorPageUrl 時,一旦用戶請求線上應用的 HTML 頁面異常,就會重定向到這個地址。

在 config/config.default.js 中

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

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

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

// config/config.default.js
module.exports = {
onerror: {
all(err, ctx) {
// 在此處定義針對所有響應類型的錯誤處理方法
// 注意,定義了 config.all 之后,其他錯誤處理方法不會再生效
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) {
// 一般來說,不需要特殊針對 jsonp 進行錯誤定義,jsonp 的錯誤處理會自動調用 json 錯誤處理,并包裝成 jsonp 的響應格式
},
},
};

404

框架并不會將服務端返回的 404 狀態(tài)當做異常來處理,但是框架提供了當響應為 404 且沒有返回 body 時的默認響應。

  • 當請求被框架判定為需要 JSON 格式的響應時,會返回一段 JSON:{ "message": "Not Found" }
  • 當請求被框架判定為需要 HTML 格式的響應時,會返回一段 HTML:<h1>404 Not Found</h1>

框架支持通過配置,將默認的 HTML 請求的 404 響應重定向到指定的頁面。

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

自定義 404 響應

在一些場景下,我們需要自定義服務器 404 時的響應,和自定義異常處理一樣,我們也只需要加入一個中間件即可對 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' ],
};


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號