เกิดข้อผิดพลาดในการจัดการหลักการสำหรับแอปพลิเคชัน Node.js + Express.js หรือไม่


177

ดูเหมือนว่าการรายงาน / การจัดการข้อผิดพลาดจะทำแตกต่างกันในแอปพลิเคชันNode.js + Express.jsเมื่อเทียบกับเฟรมเวิร์กอื่น ๆ ฉันถูกต้องในการทำความเข้าใจว่ามันทำงานได้ดังต่อไปนี้?

A) ตรวจจับข้อผิดพลาดโดยรับเป็นพารามิเตอร์ในฟังก์ชั่นการโทรกลับของคุณ ตัวอย่างเช่น:

doSomethingAndRunCallback(function(err) { 
    if(err) {  }
});

B) รายงานข้อผิดพลาดใน MIDDLEWARE โดยโทรถัดไป (err) ตัวอย่าง:

handleRequest(req, res, next) {
    // An error occurs…
    next(err);
}

C) รายงานข้อผิดพลาดใน ROUTES โดยการโยนข้อผิดพลาด ตัวอย่าง:

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

D) จัดการข้อผิดพลาดโดยการกำหนดค่าตัวจัดการข้อผิดพลาดของคุณเองผ่าน app.error () หรือใช้ตัวจัดการข้อผิดพลาดการเชื่อมต่อทั่วไป ตัวอย่าง:

app.error(function(err, req, res, next) {
    console.error(err);
    res.send('Fail Whale, yo.');
});

หลักการทั้งสี่นี้เป็นพื้นฐานสำหรับการจัดการข้อผิดพลาด / การรายงานทั้งหมดในแอปพลิเคชัน Node.js + Express.js หรือไม่

คำตอบ:


183

การจัดการข้อผิดพลาดใน Node.js โดยทั่วไปเป็นรูปแบบ A) nullเรียกกลับส่วนใหญ่กลับวัตถุข้อผิดพลาดเป็นอาร์กิวเมนต์แรกหรือ

Express.js ใช้มิดเดิลแวร์และมิดเดิลแวร์ไวยากรณ์ใช้ B) และ E) (กล่าวถึงด้านล่าง)

C) เป็นการปฏิบัติที่ไม่ดีถ้าคุณถามฉัน

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

คุณสามารถเขียนซ้ำข้างต้นได้อย่างง่ายดายเช่น

app.get('/home', function(req, res, next) {
    // An error occurs
    next(err);
});

ไวยากรณ์ของมิดเดิลแวร์นั้นถูกต้องในgetคำขอ

สำหรับ D)

(07:26:37 PM) tjholowaychuk: app.error ถูกลบใน 3.x

TJ เพิ่งยืนยันว่าapp.errorไม่เห็นด้วยกับ E

E)

app.use(function(err, req, res, next) {
  // Only handle `next(err)` calls
});

มิดเดิลแวร์ใด ๆ ที่มีความยาว 4 (4 อาร์กิวเมนต์) ถือเป็นมิดเดิลแวร์ข้อผิดพลาด เมื่อมีการnext(err)เชื่อมต่อหนึ่งสายไปและเรียกมิดเดิลแวร์ที่มีข้อผิดพลาด


11
ขอบคุณ! สำหรับใครก็ตามที่อาจเจอสิ่งนี้ในอนาคตดูเหมือนว่าลำดับของ params สำหรับ "เมธอด e" นั้นแท้จริงแล้วคือ err, req, res, next (แทนที่จะเป็น req, res, next, err)
Clint Harris

9
ดังนั้นสิ่งนี้จึงดูดี แต่ปัญหาที่ฉันเห็นคือข้อผิดพลาดบางอย่างไม่เคยทำให้เกิดปัญหากับตัวจัดการข้อผิดพลาดที่คุณอธิบายและจัดการได้โดย process.on ('uncaughtException', fn) เท่านั้น ภูมิปัญญาดั้งเดิมคือการปล่อยให้สิ่งนั้นเกิดขึ้นและพึ่งพา Forever หรือรีสตาร์ทแอพ แต่ถ้าคุณทำเช่นนั้นคุณจะส่งคืนหน้าข้อผิดพลาดที่เป็นมิตรได้อย่างไร
พอล

1
@ chovy ยังเป็นเพียงแค่ fyi ตัวจัดการข้อผิดพลาดจะต้องมอบให้กับแอปหลังจากข้อผิดพลาดการโยน / ถัดไป หากเป็นก่อนหน้านี้จะไม่ตรวจจับข้อผิดพลาด
Lee Olayvar

3
ถัดไป (err) เป็นเวอร์ชั่นของ Express ในการโยนข้อผิดพลาดคุณต้องเรียกมันอย่างชัดเจนภายในมิดเดิลแวร์ของคุณเอง
qodeninja

1
@qodeninja วิธีการนั้นถือเป็นแนวปฏิบัติที่ดีที่สุดใน Express
David Oliveros

11

ผู้คนที่ Joyent ได้ตีพิมพ์เอกสารแนวปฏิบัติที่ดีที่สุดเกี่ยวกับเรื่องนี้ บทความที่ต้องอ่านสำหรับนักพัฒนา Node.js



บทความที่ดีเยี่ยมแก้ไขลิงค์เพื่อชี้ไปที่เอกสารที่อัพเดทของ Joyent
stephbu

2
บทความไม่เลว: แต่ข้อความมากเกินไปและตัวอย่างไม่เพียงพอมันเป็นบทความสำหรับมืออาชีพที่แท้จริง
Gerd

3

ทำไมพารามิเตอร์แรก

เพราะธรรมชาติไม่ตรงกันของ Node.js ที่พารามิเตอร์ตามที่ผิดพลาดครั้งแรกรูปแบบได้กลายเป็นที่ยอมรับในฐานะที่ประชุมสำหรับuserland จัดการข้อผิดพลาด นี่เป็นเพราะแบบอะซิงโครนัส:

try {
    setTimeout(function() {
        throw 'something broke' //Some random error
    }, 5)
}
catch(e) {
   //Will never get caught
}

ดังนั้นแทนที่จะมีอาร์กิวเมนต์แรกของการติดต่อกลับเป็นวิธีเดียวที่สมเหตุสมผลในการส่งข้อผิดพลาดแบบอะซิงโครนัสนอกเหนือจากการโยนทิ้ง

ในการทำเช่นนั้นจะส่งผลให้เกิดunhandled exceptionเสียงซึ่งหมายความว่าไม่มีสิ่งใดที่ทำให้แอปพลิเคชันหลุดออกจากสถานะที่สับสน

ข้อยกเว้นทำไมถึงมีอยู่

อย่างไรก็ตามเป็นที่น่าสังเกตว่าแทบทุกส่วนของ Node.js เป็นตัวส่งเหตุการณ์และการขว้างข้อยกเว้นเป็นเหตุการณ์ระดับต่ำซึ่งสามารถจัดการได้เหมือนกับเหตุการณ์ทั้งหมด:

//This won't immediately crash if connection fails
var socket = require("net").createConnection(5000);
socket.on("error", function(err) {
    console.error("calm down...", err)
});

สิ่งนี้สามารถ - แต่ -ไม่ควรถูกนำไปสุดขีดเพื่อตรวจจับข้อผิดพลาดทั้งหมดและสร้างแอปพลิเคชันซึ่งจะพยายามอย่างหนักที่จะไม่ผิดพลาด นี่เป็นความคิดที่แย่มากในเกือบทุกกรณีการใช้งานเพราะมันจะทำให้นักพัฒนาซอฟต์แวร์ไม่มีความคิดว่าเกิดอะไรขึ้นในสถานะแอปพลิเคชัน

โดเมน - การจัดกลุ่มเหตุการณ์อย่างมีเหตุผล

ในฐานะที่เป็นส่วนหนึ่งของการจัดการกับปัญหาข้อยกเว้นที่ทำให้แอปพลิเคชันล้มเหลวโดเมนอนุญาตให้นักพัฒนาใช้ตัวอย่างเช่นแอปพลิเคชัน Express.js และลองและปิดการเชื่อมต่ออย่างสมเหตุสมผลในกรณีที่เกิดภัยพิบัติล้มเหลว

ES6

อาจกล่าวได้ว่าสิ่งนี้จะเปลี่ยนอีกครั้งเนื่องจาก ES6 อนุญาตให้รูปแบบตัวสร้างสร้างเหตุการณ์แบบอะซิงโครนัสซึ่งยังสามารถจับได้ด้วยบล็อกแบบลอง / จับ

Koa (เขียนโดย TJ Holowaychuck ผู้เขียนต้นฉบับ Express.js คนเดียวกัน) ทำสิ่งนี้อย่างเห็นได้ชัด มันใช้yieldคำสั่งES6 เพื่อสร้างบล็อกที่มีการจัดการแบบซิงโครนัสตามปกติในขณะที่ปรากฏเกือบจะเป็นแบบซิงโครนัส:

app.use(function *(next) {
    try {
        yield next;
    } 
    catch (err) {
        this.status = err.status || 500;
        this.body = err.message;
        this.app.emit('error', err, this);
    }
});

app.use(function *(next) {
    throw new Error('some error');
})

ตัวอย่างนี้ถูกขโมยลงคอจากที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.