Koa異常處理說明

Koa異常處理說明

作者:zjruan 日期:2017/07/10

使用篇:

Demo:

// mc/homework
// 個(gè)人中心-我的課程-課后作業(yè)

get_homework() {
    // 你的處理邏輯 start
    ...
    let lessonId = this.query.lessonid;
    let resBody = yield courseMemberCenterApi.getHomework(lessonId);
    ...

    yield this.render('page/mc/homework-index', { title: '課后作業(yè)', leftNavIndex: 'myCourse', exampaper: exampaper })
    // 你的處理邏輯 end
}

一哮伟、Controller 中 try catch 的使用

由于項(xiàng)目在Koa頂層有統(tǒng)一的異常處理,因此正常情況下,我們是需要編寫 try catch 的耕赘,除非我們需要做對(duì)這個(gè)異常進(jìn)行編輯辩棒,或者是這個(gè)異常我們自己能處理,不希望該異常冒泡到統(tǒng)一異常處理函數(shù)羡玛。

    // 1别智、對(duì)異常進(jìn)行修改,并拋出修改后的異常
    get_homework() {
        try{
            // 你的處理邏輯
            ......
        }catch(error){
            error.message = '這個(gè)異常我知道稼稿,是后端數(shù)據(jù)庫中的臟數(shù)據(jù)引起的薄榛!';
            throw error;    // 拋出處理后的異常
        }
    }

    // 2、已知異常让歼,自己處理而不希望拋出到上層
    get_homework() {
        try{
            // 你的處理邏輯
            ......
        }catch(error){
            error.message = '這個(gè)異常我知道敞恋,是后端數(shù)據(jù)庫中的臟數(shù)據(jù)引起的!';
            // console.log('我知道這么處理谋右,而且我不想讓別人知道');
            // this.redirct('login')    // 異常處理邏輯
        }
    }

二硬猫、如何生成自己的異常

異常在各種開發(fā)語言中都有廣泛的應(yīng)用,適時(shí)的拋出有效的異掣闹矗可以幫助開發(fā)者理解程序邏輯浦徊。例如:

get_homework() {
    // 你的處理邏輯 start
    ...
    let lessonId = this.query.lessonid;
    // 由于后續(xù)邏輯嚴(yán)重依賴 lessonid,當(dāng) lessonid 無效時(shí),后續(xù)操作便沒有意義天梧,而且耽誤時(shí)間盔性。
    // 因此對(duì)依賴參數(shù)進(jìn)行有效性檢查是必要的
    if(!lessonId){
        this.throw('400', 'lessonid 無效');
    }

    let resBody = yield courseMemberCenterApi.getHomework(lessonId);
    ...

    yield this.render('page/mc/homework-index', { title: '課后作業(yè)', leftNavIndex: 'myCourse', exampaper: exampaper })
    // 你的處理邏輯 end
}

拋出異常語法:

    // ctx 環(huán)境變量
    // statusCode http錯(cuò)誤碼,錯(cuò)誤碼是有限制的呢岗,無效的錯(cuò)誤碼會(huì)被替換為500冕香,錯(cuò)誤碼請(qǐng)看附錄
    // message 異常描述蛹尝,可選
    // param 異常攜帶的參數(shù), 可選
    ctx.throw(statusCode [, message], [param])

koa uses http-errors to create errors悉尾,點(diǎn)過去看看突那。


Koa 統(tǒng)一異常處理

統(tǒng)一異常處理的好處不用多說,那么在 Koa 中构眯,如何使用愕难?

我們先查看 Koa 的 API 文檔,找到錯(cuò)誤處理段落惫霸,原文如下:

Error Handling

By default outputs all errors to stderr unless app.silent is true. The default error handler also won't outputs errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

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

If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

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

When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond appropriately with a 500 "Internal Server Error". In either case an app-level "error" is emitted for logging purposes.


因此猫缭,我們只需要給 Koa 添加一個(gè)異常監(jiān)聽事件并且處理這個(gè)事件就可以了。

// 注意:以下代碼為偽代碼壹店,無法直接復(fù)制運(yùn)行

// app.js
import errorHandler from 'middleware/errorHandler.js'
...
app.on(error, errorHandler);


// errorHandler.js
module.exports = function(err, ctx) {
    // 未知異常狀態(tài)猜丹,默認(rèn)使用 500
    if(!err.status) err.status = 500;
    ctx.status = err.status;

    // 獲取客戶端請(qǐng)求接受類型
    let acceptedType = ctx.accepts('html', 'text', 'json');

    switch(acceptedType){
        case 'text': 
            ctx.type = 'text/plain';
            ctx.body = err.message;
            break;
        case 'json': 
            ctx.type = 'application/json';
            ctx.body = {error: err.message}
            break;
        case 'html': 
        default: 
            // 默認(rèn)返回頁面
            ctx.type = 'text/html';
            ctx.redirect(getUrl(err.status));
            break;
    }

    /**
    * 根據(jù) Http 狀態(tài)碼,獲取重定向頁面
    *
    * param {number} status http狀態(tài)碼
    */
    function getUrl(status){
        switch(error.status){
            case 401: url = '401.html'; break;
            case 404: url = '404.html'; break;
            case 500: url = '500.html'; break;
            case 502: url = '502.html'; break;
            default:
                if(err.status < 500) {
                    url = '40x.html';
                } else {
                    url = '50x.html';
                    ctx.redirect('50x.html'))
                }
        }
        return url;
    }
} 
    

到了這一步應(yīng)該就差不多了硅卢,思路是添加一個(gè)全局的異常監(jiān)聽事件射窒,當(dāng)異常發(fā)生時(shí),對(duì)其進(jìn)行統(tǒng)一的處理将塑。

根據(jù)客戶端接受數(shù)據(jù)類型脉顿,區(qū)分返回格式,對(duì)于請(qǐng)求頁面的点寥,我們最好能重定向到相應(yīng)的頁面艾疟,區(qū)分 404 和 500 等異常頁面,對(duì)用戶來說會(huì)更加友好绒窑。

由于異常頁面數(shù)量有限镐牺,直接寫成靜態(tài)頁面即可,雖然節(jié)約的性能并不明顯,主要是 UI 可能會(huì)提供不同風(fēng)格的異常頁面闪盔,方便定制。

404 頁面

所謂的統(tǒng)一異常處理售躁,無非就是在程序的最外層价匠,包上一層try catch,以至于所有的異常都會(huì)被這個(gè)catch給catch住嘀略。但是我們注意上面的那段文檔中有一句話:

default error handler also won't outputs errors when err.status is 404 or err.expose is true恤溶。

大致意思是 404 是不會(huì)拋出異常的,也就是說我們寫的這個(gè) errorHandler 不會(huì)處理 404頁面帜羊。
那怎么辦咒程?少里 404 的異常處理還叫統(tǒng)一的異常處理么?

顯然不是讼育,所以我們得想辦法解決帐姻。那為什么404不拋出異常呢稠集?因?yàn)?404 是服務(wù)器沒有找到訪問目標(biāo),也就是 Koa 路由沒有匹配到對(duì)于的url饥瓷,并不是執(zhí)行異常剥纷,因此無法被 try catch catch住。
因?yàn)樗皇钱惓D孛圆荒鼙籧atch住晦鞋,那么我們就為的給它拋出異常,這樣問題不就解決了棺克。

我們需要在 koa-router 加載之前悠垛,添加一個(gè)中間件,如下:

app.use(function* (next) {
    yield* next;
    if (this.response.status === 404 && !this.response.body) this.throw(404);
});

這樣相當(dāng)于在 koa-router 后面添加了一層判斷逆航。當(dāng)路由匹配失敗后鼎文,判斷返回是不是404,如果是的因俐,我們就手工拋出 404異常, 這樣就能被我們的統(tǒng)一異常處理方案處理了拇惋。

附錄:

Status Code Constructor Name
400 BadRequest
401 Unauthorized
402 PaymentRequired
403 Forbidden
404 NotFound
405 MethodNotAllowed
406 NotAcceptable
407 ProxyAuthenticationRequired
408 RequestTimeout
409 Conflict
410 Gone
411 LengthRequired
412 PreconditionFailed
413 PayloadTooLarge
414 URITooLong
415 UnsupportedMediaType
416 RangeNotSatisfiable
417 ExpectationFailed
418 ImATeapot
421 MisdirectedRequest
422 UnprocessableEntity
423 Locked
424 FailedDependency
425 UnorderedCollection
426 UpgradeRequired
428 PreconditionRequired
429 TooManyRequests
431 RequestHeaderFieldsTooLarge
451 UnavailableForLegalReasons
500 InternalServerError
501 NotImplemented
502 BadGateway
503 ServiceUnavailable
504 GatewayTimeout
505 HTTPVersionNotSupported
506 VariantAlsoNegotiates
507 InsufficientStorage
508 LoopDetected
509 BandwidthLimitExceeded
510 NotExtended
511 NetworkAuthenticationRequired
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市抹剩,隨后出現(xiàn)的幾起案子撑帖,更是在濱河造成了極大的恐慌,老刑警劉巖澳眷,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胡嘿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钳踊,警方通過查閱死者的電腦和手機(jī)衷敌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拓瞪,“玉大人缴罗,你說我怎么就攤上這事〖拦。” “怎么了面氓?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蛆橡。 經(jīng)常有香客問我舌界,道長(zhǎng),這世上最難降的妖魔是什么泰演? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任呻拌,我火速辦了婚禮,結(jié)果婚禮上睦焕,老公的妹妹穿的比我還像新娘柏锄。我一直安慰自己酿箭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布趾娃。 她就那樣靜靜地躺著缭嫡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抬闷。 梳的紋絲不亂的頭發(fā)上妇蛀,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音笤成,去河邊找鬼评架。 笑死,一個(gè)胖子當(dāng)著我的面吹牛炕泳,可吹牛的內(nèi)容都是我干的纵诞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼培遵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼浙芙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起籽腕,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤嗡呼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后皇耗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南窗,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年郎楼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了万伤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呜袁,死狀恐怖敌买,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情傅寡,我是刑警寧澤放妈,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布北救,位于F島的核電站荐操,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏珍策。R本人自食惡果不足惜托启,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望攘宙。 院中可真熱鬧屯耸,春花似錦拐迁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至多矮,卻和暖如春缓淹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塔逃。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工讯壶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人湾盗。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓伏蚊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親格粪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躏吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 原文鏈接:http://www.reibang.com/p/6b816c609669 前傳 出于興趣最近開始研究k...
    懸筆e絕閱讀 7,203評(píng)論 1 11
  • 1.簡(jiǎn)書 koa是由Express原班人馬打造,致力于成為一個(gè)更小匀借、更富有表現(xiàn)力颜阐、更健壯的Web框架。使用koa編...
    不去解釋閱讀 2,652評(píng)論 0 11
  • 陸陸續(xù)續(xù)用了koa和co也算差不多用了大半年了吓肋,大部分的場(chǎng)景都是在服務(wù)端使用koa來作為restful服務(wù)器用凳怨,使...
    Sunil閱讀 1,528評(píng)論 0 3
  • 人是要不斷學(xué)習(xí)的,煩擾的世界有一條清晰的平靜的時(shí)間線是鬼,不因世俗而有所改變肤舞,這因便是真理了吧。但大多數(shù)人不了解這個(gè)真...
    你大爺?shù)拇竽?/span>閱讀 125評(píng)論 0 3
  • 情不知所起均蜜,一往而深李剖。 原來姹紫嫣紅開遍,似這般都付與斷井頹垣囤耳,良辰美景奈何天篙顺,賞心樂事誰家院?
    開花的蟋蟀閱讀 198評(píng)論 0 0