نحوه رسیدگی به خطا‌ها در یک برنامه Nodejs و Express

ترجمه و تالیف : مهدی عقیقی
تاریخ انتشار : 18 اسفند 98
خواندن در 3 دقیقه
دسته بندی ها : نود جی اس

وقتی ما با استفاده از Express، Api درست می‌کنیم، Route ها و متد‌های مربوط به رسیدگی به آن‌ها را هم درست می‌کنیم. در یک دنیای ایده‌آل، مصرف‌کننده‌های Api ما درخواست‌هایی به Route هایی که ما تعریف کرده‌ایم می‌زنند و بدون ارور کار‌شان را انجام می‌دهند. اما همان‌طور که می‌دانید، ما در یک دنیای ایده‌آل زندگی نمی‌کنیم. Express این را می‌داند و رسیدگی به ارور‌ها را در Api ما ساده می‌کند.

در این مقاله، من به شما توضیح می‌دهم که چگونه در Express به ارور‌ها رسیدگی کنید. برای شروع لطفا این پروژه را clone کنید. و یادتان باشد که بعد از clone کردن npm install را بزنید.

این پروژه یک فایل index.js با محتویات زیر دارد.

const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res, next) => {
 res.send("Welcome to main route!");
});

app.get("/about", (req, res, next) => {
 res.send("This is the about route!");
});

app.listen(port, () => console.log(`App listening on port: ${port}`));

اگر نمی‌خواهید پروژه را clone کنید، یک فولدر جدید بسازید و در آن npm init -y را و سپس npm i --save express را بزنید. و سپس فایل index.js را بسازید و کد‌های بالا را در آن کپی کنید.

منبع ارور‌ها

دو راه اساسی وجود دارد که ممکن است در برنامه‌ی Express خطا رخ دهد.

یک راه این است که درخواست کاربر‌ها به route هایی بیاید که هیچ متد مخصوصی ندارند (handler) برای مثال ما در index.js دو Route تعریف می‌کنیم. (یکی به / و یکی به /about). من از Route‌ های get استفاده می‌کنم پس می‌توانیم راحت آن‌ها را در مرورگر تست کنیم. توجه کنید که هر Route یک مسیر و یک middleware (برای استفاده در زمانی که Route خوانده شد) دارد.

app.HTTPMethod(path, middleware)
// HTTPMethod = get, post, put, delete …

 یک منبع ارور دیگر وقتی است که چیزی در فانکشن رسیدگی به Route یا جای دیگر کد ما درست پیش نرود. برای مثال، اولین Route در در فایل Index.js را با استفاده از کد زیر آپدیت کنید.

…
app.get(‘/’, (req, res, next) => {
 // mimic an error by throwing an error to break the app!
 throw new Error(‘Something went wrong’);
 res.send(‘Welcome to main route!’)
})
…

سرور را ری‌استارت کنید و localhost:3000 را باز کنید، شما با یک ارور مواجه خواهید شد.

رسیدگی به خطا‌های مسیریابی با ترتیب آن‌ها

آن بخش از کد که ارور را می‌دهد پاک کنید و سرور را ری‌استارت کنید و وارد localhost:3000 شوید. باید با پیغام زیر مواجه شوید.

Welcome to the main route!

و وقتی وارد localhost:3000/about شوید باید با پیغام زیر مواجه شوید..

This is the about route!

Express چگونه مسیر‌ها را جست و جو می‌کند؟

Express چیزی به اسم routing table یا جدول مسیر‌ها درست می‌کند. که در آن مسیر‌ها را به ترتیبی که تعریف شده‌اند در آن قرار می‌دهد. وقتی درخواست به وب‌سرور می‌رسد، url به جدول مسیر‌ها می‌رود و اولین مسیر که با url تطابق داشته باشد را اجرا می‌کند. حتی اگر بیش‌تر از یک تطابق وجود داشته باشد.

اگر هیچ تطابقی وجود نداشته باشید، express به شما یک ارور نمایش می‌دهد. برای دیدن این ارور وارد localhost:3000/contact شوید، و مرورگر به شما ارور زیر را نشان می‌دهد.

Cannot GET /contact

بعد از چک کردن جدول مسیر‌ها، express تطابقی پیدا نکرده است، پس پیغام بالا را نمایش داده است.

نحوه بهره‌برداری از ترتیب مسیر‌ها

حالا که وقتی express در جدول مسیر‌ها تطابقی با url پیدا نمی‌کند، یک پیغام ارور نمایش می‌دهد. پس به این معنا است که ما باید مسیری برای رسیدگی به این خطا تعریف کنیم، به طوری که مطمئن باشیم که این مسیر، آخرین مسیر در برنامه‌ی ما است.

به این دلیل که ما دقیقا نمی‌دانیم کاربر چه urlی می‌نویسد که در برنامه‌ی ما موجود نیست، نمی‌توانیم یک مسیر خاص برای این قضیه تعریف کنیم. و حتی ما این را هم نمی‌دانیم که این درخواست با چه متدی قرار است ارسال شود. در این حالت ما می‌توانیم از app.use() به جای app.get() استفاده کنیم.

فایل index.js را با گزاشتن کد زیر در آخر همه‌ی Route ها و قبل از app.listen() آپدیت کنید. 

…
// this matches all routes and all methods
app.use((req, res, next) => {
 res.status(404).send({
 status: 404,
 error: ‘Not found’
 })
})
app.listen(port …

سرور را ری‌استارت کنید و یک مسیر که در برنامه شناخته شده نیست را وارد کنید. برا مثال localhost:3000/blog 

حالا ما یک ارور شخصی‌سازی شده داریم.

{“status”:404,”error”:”Not found”}

یادتان باشد که ترتیب Route ها برای این کار بسیار مهم است. اگر Routeی که گزاشتیم را در بالای بقیه Route‌ ها می‌گذاشتیم، همه‌ی مسیر‌ها با آن هم‌خوانی پیدا می‌کرد و آن را اجرا می‌کرد. پس متد رسیدگی‌کننده به ارور‌ها باید در آخر باشد.

رسیدگی به بقیه‌ی ارور‌ها

روشی که در بخش قبل در پیش گرفتیم، فقط برای ارور‌هایی بود که کاربر به مسیر ناموجود درخواست می‌زد. و  به ارور‌های دیگری که ممکن است در برنامه ما رخ دهد، رسیدگی نمی‌کند. پس راه ما هنوز کامل نشده است. و راه بالا فقط نصف مشکل را حل می‌کند.

Index.js را آپدیت کنید و کد ارور دادن زیر را به آن اضافه کنید.

…
app.get(‘/’, (req, res, next) => {
 throw new Error(‘Something went wrong!’);
 res.send(‘Welcome to main route!’)
})
…

اگر شما localhost:3000 را نگاهی بی‌اندازید، همچنان اروری که خود express می‌دهد را مشاهده می‌کنید.

تعریف کردن یک middleware برای رسیدگی به ارور‌ها

Middleware های رسیدگی به ارور مانند بقیه middleware ها تعریف می‌شوند. تنها تفاوت‌شان این است که به جای ۳ آرگومان، ۴ آرگومان دارند. برای مثال: 

// error handler middleware
app.use((error, req, res, next) => {
 console.error(error.stack);
 res.status(500).send(‘Something Broke!’);
})

این کد را پس از تعریف Route های خود در فایل index.js بگذارید. و سپس سرور را ری‌استارت کرده و localhost:3000 را چک کنید و می‌بینید که پاسخ این است:

Something Broke!

حالا ما داریم به هر دو نوع ارور رسیدگی می‌کنیم. 

این روش کار می‌کند، اما چگونه می‌توانیم آن را بهبود دهیم؟

وقتی شما یک آرگومان به next() می‌دهید، express این نتیجه را می‌گیرد که یک ارور موجود است، و همه‌ی Route ها را رد می‌کند تا به middleware ی که ارور‌ها را هندل می‌کند برسد.

Index.js را با کد زیر آپدیت کنید:

…
app.use((req, res, next) => {
 const error = new Error(“Not found”);
 error.status = 404;
 next(error);
});
// error handler middleware
app.use((error, req, res, next) => {
  res.status(error.status || 500).send({
   error: {
   status: error.status || 500,
   message: error.message || ‘Internal Server Error’,
  },
 });
});
…

آن middleware ی که ارور‌های ۴۰۴ و اشتباه بودن مسیر را می‌گرفت، حالا ارور را به middlware ارور‌ها می‌دهد. در واقع (next(error  بر این دلالت دارد که :  آهای middleware رسیدگی به ارور‌ها، من یک ارور گرفته‌ام و تو باید به آن رسیدگی کنی.

کد error.status || 500 باعث می‌شود که اگر آبجکت ارور هیچ کد status ی نداشته باشد، به صورت خودکار ۵۰۰ را برای آن بگذارد.

و فایل پایانی index.js: 

const express = require("express");

const app = express();
const port = 3000;

app.get("/", (req, res, next) => {
  throw new Error("Something went wrong!");
  res.send("Welcome to main route!");
});

app.get("/about", (req, res, next) => {
  res.send("This is the about route!");
});

app.use((req, res, next) => {
  const error = new Error("Not found");
  error.status = 404;
  next(error);
});

// error handler middleware
app.use((error, req, res, next) => {
    res.status(error.status || 500).send({
      error: {
        status: error.status || 500,
        message: error.message || 'Internal Server Error',
      },
    });
  });

app.listen(port, () => console.log(`App listening on port: ${port}`));

اگر شما به جای ارسال پاسخ JSON از صفحات HTML استفاده می‌کنید، منطق برنامه همچنان همین‌طور است. تنها کاری که باید بکنید این است که اتفاقاتی که درون middleware رسیدگی به ارور می‌افتد را تغییر دهید. برای مثال: 

app.use((error, req, res, next) => {
 console.error(error); // log an error
 res.render(‘errorPage’) // Renders an error page to user!
});

اگر به نظر شما این مقاله مفید بود با بقیه به اشتراک بگذارید و لطفا نظرات خود را در بخش نظرات اعلام کنید.

منبع

گردآوری و تالیف مهدی عقیقی

برنامه‌نویس وب، عاشق جاوااسکریپت و ریکت و لاراول :)