برنامه نویسی

دست زدن به خطا و میانه – جامعه dev

  • متغیرهای محیطی
  • میانه
  • اعتبار سنج
  • خطای خط میانی

در گزیده قبلی ، اعتبار سنجی ، احراز هویت و مجوز با کتابخانه ها ، ما از Joi برای اعتبار سنجی درخواست و JSONWeBtoken برای تولید نشانه های AUTH استفاده کردیم.

متغیرهای محیطی

هنگامی که ما JWT را برای نشانه های نویسنده خود تولید می کردیم ، یک راز را پشت سر گذاشتیم. معمولاً ، طبق توصیه ، راز به عنوان یک متغیر محیطی منتقل می شود. متغیر محیطی یک مقدار پیکربندی (مقدار کلید) است که از سیستم عامل به آن منتقل می شود یا به آن دسترسی پیدا می کند. با این کار ، ما می توانیم داده ها را به API خود منتقل کنیم بدون اینکه مقدار پیکربندی را در API تعبیه کنیم (به عنوان یک تحت اللفظی در معرض).

کنسول/ترمینال خود را باز کنید و وارد شوید ، nodeبشر روی گره جایگزین ، وارد شوید ، process.envبشر این همان چیزی است که من می بینم:

Users-MacBook-Pro:expense-tracker-simple-api user$ node
Welcome to Node.js v22.12.0.
Type ".help" for more information.
> process.env
{
  NVM_INC: '/Users/user/.nvm/versions/node/v22.12.0/include/node',
  TERM_PROGRAM: 'vscode',
  NVM_CD_FLAGS: '',
  TERM: 'xterm-256color',
  SHELL: '/bin/bash',
  HOMEBREW_REPOSITORY: '/opt/homebrew',
  TMPDIR: '/var/folders/p8/60cwd37n1ds64r381rjkt8g80000gn/T/',
  TERM_PROGRAM_VERSION: '1.97.2',
  ORIGINAL_XDG_CURRENT_DESKTOP: 'undefined',
  MallocNanoZone: '0',
  NVM_DIR: '/Users/user/.nvm',
 ...
}
>
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما می توانیم با انجام مقداری مقدار پیکربندی به API گره خود منتقل کنیم KEY_1=VALUE_1 KEY_2=VALUE_2 nodeبشر وقتی این دستور را اجرا کردیم ، دیدیم که این مقادیر کلیدی تازه اضافه شده این بار به عنوان بخشی از پاسخ ما ظاهر می شوند.

NB: شما باید از repr node استفاده کنید control + C

Users-MacBook-Pro:expense-tracker-simple-api user$ KEY_1=VALUE_1 KEY_2=VALUE_2 node
Welcome to Node.js v22.12.0.
Type ".help" for more information.
> process.env
{
  KEY_1: 'VALUE_1',
  KEY_2: 'VALUE_2',
  NVM_INC: '/Users/user/.nvm/versions/node/v22.12.0/include/node',
  NVM_CD_FLAGS: '',
  TERM: 'xterm-256color',
  SHELL: '/bin/bash',
  HOMEBREW_REPOSITORY: '/opt/homebrew',
  TMPDIR: '/var/folders/p8/60cwd37n1ds64r381rjkt8g80000gn/T/',
  TERM_PROGRAM_VERSION: '1.97.2',
  ORIGINAL_XDG_CURRENT_DESKTOP: 'undefined',
  MallocNanoZone: '0',
  NVM_DIR: '/Users/user/.nvm',
  ...
}
>
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

شما باید ببینید KEY_1: 'VALUE_1' وت KEY_2: 'VALUE_2'بشر

برای دیدن اینکه چگونه این کار در API ما کار خواهد کرد ، می توانیم یک فایل موقت ایجاد کنیم و محتوای آن را چاپ کنیم process.env

ما می توانیم با استفاده از کلید به مقادیر منتقل شده دسترسی پیدا کنیم. بنابراین اگر یک راز را پشت سر گذاشتیم ، SECRET="some secret" node temp_file.js و ما وارد می شویم ، console.log(process.env.SECRET)بشر خواهیم دید که خروجی با مقدار منتقل شده مطابقت دارد.

SECRET="some secret" node temp_file.js
some secret
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این رویکرد غیر عملی می شود به همین ترتیب ما از a استفاده می کنیم .env پرونده env = environmentبشر یک فایل را در ریشه پروژه خود ایجاد کنید و آن را صدا کنید .envبشر تمام پیکربندی های ارزش کلید خود را در آنجا عبور دهید.

پیش از nodejs 20، ما برای دسترسی به ما به یک کتابخانه نیاز داشتیم .env مقادیر بعداً در مورد آن بحث خواهیم کرد. با این حال ، برای همه نسخه های فوق ، تنها کاری که باید انجام دهیم این است که تصویب شود --env-file پرونده و مقدار آن را روی مسیر تنظیم کنید .env مسیر پرونده: node --env-file=.env temp_file.js

node --env-file=.env temp_file.js
some secret
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اگر به غیر از مقادیر واقعی تصویب شده ، چیزی را دریافت می کنید ، می توانیم این کار را با کتابخانه ای به نام Dotenv حل کنیم. شما باید بدانید که چگونه بسته ها را در Nodejs نصب کنید. نام بسته است dotenvبشر

npm i dotenv

در پرونده ما می توانیم اضافه کنیم ، require("dotenv/config")، در بالای پرونده خود. این بار ما فقط اجرا می کنیم ، node temp_file.js

require("dotenv").config();
// if the above doesn't work for you, try the approach below
// require("dotenv/config")

console.log(process.env.SECRET);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اگر با git آشنا هستید ، حتماً مسیر را به سمت آن ایجاد کرده و عبور دهید .env به .gitignore
برای شما که با Git آشنا نیستید ، Git ابزاری برای مدیریت کد منبع شماست.
.env مقادیر پیکربندی واردات را در اختیار دارد و در معرض آنها قرار می گیرد و منجر به بهره برداری بالقوه می شود. مراقب باشید اما وحشت نکنید.

با استفاده از این اطلاعات ، اکنون شما ایجاد یک .env پرونده هر مقدار پیکربندی را منتقل می کند و به آن طبق برنامه دسترسی پیدا می کند.

فقط برای ایمن بودن ، بررسی کنید که آیا مقدار است undefined و به درستی پاسخ دهید – در مورد تفاوت بین NULL و تعریف شده بخوانید

از آنجا که ما مجبور نیستیم خودمان را بسازیم .env باز ، ما ایجاد می کنیم sample.env فقط کلیدها را ثبت کرده و عبور دهید تا توسعه دهندگان بدانند که چه چیزی .env پرونده انتظار دارد

میانه

ما بحث کرده ایم که کنترل کننده چیست. این یک یادآوری است ، و شاید اگر ما در مورد آن بحث نکرده ایم ، یک کنترلر یک تابع (روش) است که درخواست ها را کنترل می کند ، از فرم ها است:

[async] function FunctionName(requestObject, responseObject) {
    // do something on the request
    // return a response
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نکته ای که من می خواهم درک کنیم این است که کنترل کننده ها متقاضی درخواست کننده هستند.

متقاضیان درخواست معمولاً به دو شکل هستند:

به طور خلاصه ، یک واسطه نرم افزاری درخواست کننده است که بین یک مسیر و یک کنترلر قرار دارد.

router.method("/some route", middlewares, controller);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

یک میان افزار به این شکل است:

[async] function FunctionName(requestObject, responseObject, nextMiddleware) {
    // do something on the request
    // return a response if there is an error
    // else go to the next middleware
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

تنها تفاوت در اینجا این است که یک پارامتر سوم دارد که لقب دارد ، next، بیشتر اوقات next در اینجا میان افزار بعدی در خط لوله وجود دارد (کنترلر مانند یک واسطه است).

یک واسطه به شیء درخواست و پاسخ دسترسی دارد و به همین ترتیب می تواند درخواست تغییر (حذف ، افزودن یا به روزرسانی) بار را رهگیری کند. در ما index.js پرونده ، موارد زیر را داریم:

// parse request body as json
app.use(express.json());

// register routers
app.use(userEndpoints);
app.use(expendituresEndpoints);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • app.use(express.json()); واسطه ای است که بدنه درخواست را به JSON تجزیه می کند
  • app.use(userEndpoints); که نقاط انتهایی کاربر را در برنامه اصلی قرار می دهد
  • app.use(expendituresEndpoints); که نقاط پایانی هزینه ها را در برنامه اصلی قرار می دهد

بیایید یک میان افزار ایجاد کنیم که موارد زیر را در مورد یک درخواست ثبت کند:

  • زمان
  • روش HTTP
  • و مسیر بازدید مشتری را مسیریابی کنید

بیشتر این اطلاعات را می توان در شی درخواست یافت. req.method وت req.originalUrlبشر

// add console logging middleware
app.use((req, res, next) => {
    const log = `${req.method} :: ${
        req.originalUrl
    } - ${new Date().toISOString()}`;

    console.log(log);

    return next();
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این وسط را بعد از (یا زیر) قرار دهید app.use(express.json());بشر

نکته ای که باید بدانید این است که Middlewares به ترتیب تنظیم شده اجرا می شوند.
app.use(express.json()); قبل از ضربه هر نقطه پایانی می آید ، بنابراین داده ها قبل از رسیدن به آن مسیر تجزیه می شوند.

با توجه به این نکته ، اکنون می توانیم وسط نرم افزار را بالاتر از روترهای ثبت شده (یا مسیرها) قرار دهیم ، اما در زیر app.use(express.json());بشر

در مسیرهای محافظت شده ، مسیرهایی که به JWT Auth نیاز داشتند، ما می توانیم یک میانی نرم افزاری داشته باشیم که بررسی کند که آیا درخواست ورودی دارای نشانه JWT است یا خیر.

اگر تعجب می کنید که چه اتفاقی می افتد یا چه کاری باید انجام شود در صورت عدم وجود JWT ، باید یک پیام یا پاسخ خطا مناسب را برگردانید. موضوع پاسخ ، سر، در معرض شما قرار دارد.

ظاهراً ، هیچ یک از مسیرهای کاربر نیاز به JWT Auth ندارد ، با این حال ، هزینه ها انجام می دهند. بیایید میان افزار دیگری ایجاد کنیم که در صورت درخواست JWT بررسی کند.

بررسی کردن authorize عملکرد نحوه دسترسی به JWT

آیا این قطعه کد را دیده اید و چند نفر را دیده اید؟ حدود پنج؟

const authReponse = authorize(req.headers.authorization);
if (!authReponse.isAuthorized) {
    return res.status(200).json({
        success: false,
        message: "Unauthorized, please log in",
    });
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

وقتی مسیرهای بیشتری داشته باشیم که به JWT Auth نیاز داشته باشیم ممکن است بیشتر باشد.

این بار قصد داریم تابعی را برای این میان افزار ایجاد کنیم. دستان خود را روی آن امتحان کنید.

/* checks the header of the incoming request if there is a jwt */
function hasJwt(req, res, next) {
    // implementation before calling the next function
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

و برای انجام احراز هویت ، میانی دیگری ایجاد کنید.

/* authenticates and authorizes user */
function isAuthorized(req, res, next) {
    // implement user authentication here
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

فایلی به نام ایجاد کنید middlewares.js و اولین میانی نرم افزار را برای ورود به درخواست (جزئیات) به عنوان نامگذاری کنید logRequestبشر

(req, res, next) تا آنجا که ترتیب (موقعیت) پارامترها رعایت می شود ، می تواند هر اسامی داشته باشد.

در اینجا چیزی است که من دارم یا آنچه شما باید به نظر برسید:

// middlewares.js;
const jwt = require("jsonwebtoken");

/* Logs the request method, route and the time the request was made */
function logRequest(req, res, next) {
    const log = `${req.method} :: ${
        req.originalUrl
    } - ${new Date().toISOString()}`;

    console.log(log);

    return next();
}

/* checks the header of the incoming request if there is a jwt */
function hasJwt(req, res, next) {
    const authorization = req.headers.authorization;
    if (!authorization) {
        return res.status(200).json({
            success: false,
            message: "Unauthorized, No auth token found",
        });
    }

    return next();
}

function isAuthorized(req, res, next) {
    const authorization = req.headers.authorization;

    const JWT_SECRET = process.env.SECRET;
    if (!JWT_SECRET) {
        return res.status(200).json({
            success: false,
            message: "Something went wrong",
        });
    }

    const { userId, email } = jwt.verify(authorization, JWT_SECRET);

    // now we can fetch the user with email and userId
    const isAuthenticUser = users.find(
        (user) => user.email === email && user.id === userId
    );

    if (!isAuthenticUser) {
        return res.status(200).json({
            success: false,
            message: "Unauthorized, No user matched",
        });
    }

    req.user = { userId, email };

    return next();
}

module.exports = { logRequest, hasJwt, isAuthorized };
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

req.someProperty = someValue، کجا someProperty مقدار کلیدی موجود را بازنویسی نمی کند.
ما پیام دهنده درخواست بعدی را ارسال می کنیم و به همان شیوه به آن دسترسی پیدا می کنیم. در چرخه زندگی درخواست ، شی درخواست در کل به اشتراک گذاشته می شود.

اکنون می توانیم موارد را به روز کنیم list expenditures مسیر برای اضافه کردن hasJwt وت isAuthorizedبشر

// list expenditures
app.get("/expenditures", (req, res) => {
    // extra auth token from headers
    const authReponse = authorize(req.headers.authorization);
    if (!authReponse.isAuthorized) {
        return res.status(200).json({
            success: false,
            message: "Unauthorized, please log in",
        });
    }

    // parse the auth user id
    const { userId } = authReponse;

    // ====================
    // get the query string and check if it is not a number or something
    // that can be a number else set a default filter value of 0
    let amountMoreThan = Number(req.query.amountMoreThan);
    if (isNaN(amountMoreThan) || amountMoreThan < 0) {
        amountMoreThan = 0;
    }

    return res.json({
        success: true,
        data: expenditures.filter(
            (row) => row.userId === userId && row.amount > amountMoreThan
        ),
    });
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

می شود

// list expenditures
app.get("/expenditures", hasJwt, isAuthorized, (req, res) => {
    // parse the userId and email from the req.user
    const { userId /*,  email */ } = req.user;

    // ====================
    // get the query string and check if it is not a number or something
    // that can be a number else set a default filter value of 0
    let amountMoreThan = Number(req.query.amountMoreThan);
    if (isNaN(amountMoreThan) || amountMoreThan < 0) {
        amountMoreThan = 0;
    }

    return res.json({
        success: true,
        data: expenditures.filter(
            (row) => row.userId === userId && row.amount > amountMoreThan
        ),
    });
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نکته اینجاست که می توانیم بین مسیر و کنترل کننده درخواست (کنترل کننده) یک واسطه (ها) اضافه کنیم.

یک تمرین دیگر در اینجا انجام همین کار برای بقیه است.

  • اضافه کردن hasJwt, isAuthorized,
  • سپس حذف کنید
// extra auth token from headers
const authReponse = authorize(req.headers.authorization);
if (!authReponse.isAuthorized) {
    return res.status(200).json({
        success: false,
        message: "Unauthorized, please log in",
    });
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

// parse the auth user id
const { userId } = authReponse;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

// parse the auth user id
const { userId } = req.user;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اگر تعجب می کنید کجا req.user از آن است ، پس از آن است (یا تنظیم شده است) isAuthorized واسطه

ورود به سیستم را امتحان کنید ، و یک ورود به سیستم را مشاهده خواهید کرد ، POST :: /users/login - 2025-03-02T14:23:59.310Zبشر تاریخ ممکن است متفاوت باشد اما شما باید یک خروجی مشابه داشته باشید.

با استفاده از JWT به دست آمده در هزینه های پایانی هزینه ها موارد زیر را شامل می شود:

  • POST :: /expenditures - 2025-03-02T14:28:17.173Z
  • GET :: /expenditures - 2025-03-02T14:28:41.074Z

در این مرحله ، مسیرهای هزینه شما باید کم و بیش شبیه به نظر برسد

router.get("/route", hasJwt, isAuthorized, (req, res) => {
    /*  */
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

یک مورد که شما متوجه می شوید این است hasJwt, isAuthorized, در تمام مسیرها ظاهر می شود و ما نیز ممکن است آن را در ریشه نقطه پایانی هزینه های خود قرار دهیم.
برای مبتدیان این خوب است ، اما از آنجا که برای همه مسیرهایی که می توانیم پیش برویم ، آن را در نقطه پایانی ریشه قرار می دهیم.
در آنجا موقعیت های منحصر به فردی وجود داشت که میان افزار مسئله ای باشد ، می توانیم تصمیم بگیریم که آنها را در مسیر آسیب دیده قرار دهیم یا حذف کنیم.

بنابراین ما می توانیم آن را به عنوان ،

app.use(hasJwt, isAuthorized, expendituresEndpoints);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

یا

router.get("/route", hasJwt, isAuthorized, (req, res) => {
    /* some expenditure request handling */
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اولی بیشتر استقبال می کند اما هر یک از آنها کار می کند.

ما می توانیم پرونده فهرست خود را به روز کنیم تا پرونده فهرست خود را از آن به روز کنیم

// register routers
app.use(userEndpoints);
app.use(hasJwt, isAuthorized, expendituresEndpoints);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

به

// register routers
app.use("/users", userEndpoints);
app.use("/expenditures", hasJwt, isAuthorized, expendituresEndpoints);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

و ما وارد خواهیم شد userEndpoints وت expendituresEndpoints و سپس نقاط پایانی پایه (مسیرها) را بردارید. app.delete("/expenditures/:id",...) می شود ، app.delete("/:id",...)بشر

اعتبار سنج

ما در حال حاضر با مفهوم اعتبار سنجی آشنا هستیم ، با نوشتن آن خودمان یا استفاده از کتابخانه ای مانند جوی. ما همچنین می دانیم که می توانیم داده ها را از طریق هدرها ، رشته های پرس و جو ، درخواست پارامترهای و بدن به API خود منتقل کنیم. بنابراین در اینجا ، ما می توانیم بگوییم که انتظار می رود اعتبارسنجی انجام شود (مانند این ، داده های آن را از آن بگیرید).

بیایید از ثبت نام برای یک مطالعه موردی استفاده کنیم.

const { email, password } = req.body;

const validationResponse = authValidationSchema.validate({
    email,
    password,
});
if (validationResponse.error) {
    return res.status(200).json({
        success: false,
        message: validationResponse.error.message,
        // message: validationResponse.error.details[0].message,
    });
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • داده های اعتبار سنجی از بدن آمده است: const { email, password } = req.body;بشر
  • ما تماس می گیریم validate روش یک طرح جوی ، const validationResponse = authValidationSchema.validate({ ...})
  • گرفتن برخی از داده ها

یک وسط نرم افزار معمولی به این شکل است:

[async] function FunctionName(requestObject, responseObject, next) {
    // do something on the request
    // return a response
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

که همه ما باید در این مرحله بدانیم. نکته اینجاست که می توانیم داده ها را به میان افزار منتقل کنیم. ما تابعی را ایجاد خواهیم کرد که کنترل کننده درخواست را برگرداند.

function FunctionName(parameters) {
    return function (req, res, next) {
        /* do something */

        return next();
    };
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در مورد ما ، پارامترهای عملکرد طرح اعتبار سنجی مورد علاقه خواهند بود و در آنجا می خواهیم داده ها را از (برای اعتبار سنجی) استخراج کنیم. در اینجا درخواست املاک می تواند باشد: بدنهبا پارامتبا پرسش یا عناوینبشر

ما می توانیم این عملکرد را به هر نامی که می خواهید فراخوانی کنیم ، validation, validationMiddleware, etc

/* validation middleware: takes schema and a request property*/
function validation(schema, requestProperty) {
    return function (req, res, next) {
        const validationResponse = schema.validate(req[requestProperty]);

        if (validationResponse.error) {
            return res.status(200).json({
                success: false,
                message: validationResponse.error.message,
                // message: validationResponse.error.details[0].message,
            });
        }

        return next();
    };
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این باید آشنا به نظر برسد.

  • ما اعتبار سنجی را می گیریم schema و آن را به عنوان یک آرگومان منتقل کنید و همچنین داده ها را از طریق و از طریق آن تهیه کنید requestPropertyبشر
  • req[requestProperty]، اگر requestProperty آیا بدن نتیجه خواهد گرفت req["body"] کدام است req.bodyبشر

در ثبت نام کردن مسیر به نظر می رسد app.post("/signup", (req, res) => {...})بشر با اطلاعات جدیدی که اکنون داریم ، اعتبار سنجی را اضافه خواهیم کرد که از طرحواره عبور می کند و کنترلر را به روز می کند تا اعتبار سنجی را حذف کند (در کنترلر انجام شده است تا از کنترلر فقط برای منطق مورد نیاز استفاده شود).

در sign up مسیر باید شبیه باشد:

app.post("/signup", validation(authValidationSchema, "body"), (req, res) => {
    /*  */
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما می توانیم همان refactor را در ورودبشر

app.post("/login", validation(authValidationSchema, "body"), (req, res) => {
    /*  */
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

به عنوان یک تمرین ، مسیرهای هزینه را برای استفاده از میان افزار اعتبار سنجی عمومی و عبور از طرحواره مناسب به آن ، بازپرداخت کنید. ما حتی می توانیم با اعتبار سنجی پارامترهای درخواست و نمایش داده های رشته ای ، مایل اضافی را طی کنیم.

اینگونه است که نقطه پایانی ما به نظر می رسد:

/* /users */
app.post("/signup", validation(authValidationSchema, "body"), (req, res) => {
    /*  */
});

app.post("/login", validation(authValidationSchema, "body"), (req, res) => {
    /*  */
});

/* /expenditures*/
app.get("/", validation(expenditureQuerySchema, "query"), (req, res) => {
    /*  */
});

app.get("/:id", validation(IdValidationSchema, "params"), (req, res) => {
    /*  */
});

app.post("/", validation(createExpenseSchema, "body"), (req, res) => {
    /*  */
});

app.put(
    "/:id",
    validation(IdValidationSchema, "params"),
    validation(updateExpenseSchema, "body"),
    (req, res) => {
        /*  */
    }
);

app.delete("/:id", validation(IdValidationSchema, "params"), (req, res) => {
    /*  */
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خطای خط میانی

امیدوارم شاید ، شما متوجه شده باشید که ما این نوع خطا را بدست آورده ایم:

  • TokenExpiredError: jwt expired
  • JsonWebTokenError: invalid token

در بعضی از نقاط موظف است. به هر حال ، ما باید به طور کلی خطاهایی را که ممکن است در مصرف API (ادغام) ما رخ دهد ، انجام دهیم. یک راه برای رسیدگی به خطاها استفاده از آن است try-and-catchبشر ما در مورد بحث کردیم try-and-catch در JavaScript Essentials: قسمت 5.

ما کدی را که می دانیم می پیچیم ممکن است خطایی در یک ایجاد کند try-and-catch در صورت بروز خطایی ، پیام پیش فرض را بازگردانید و سپس برخی از پیام های پیش فرض را برگردانید. مثال:

function someRequestHandler(req, res, next) {
    try {
        /* Do something */
    } catch (error) {
        return res.status(200).json({
            success: false,
            message: "Something went wrong please, try again later.",
        });
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما حدود شش مسیر داریم و برای شروع می توانیم درست همانطور که در بالا نشان داده شد ، خطاها را برطرف کنیم. مواردی وجود دارد که موارد فوق اشکالی ندارد. با این حال ، در مورد یک برنامه تجاری ، وقتی خطایی رخ می دهد ، ما می خواهیم آن را درست بدانیم و حل کنیم؟ نکته منفی با اجرای فوق این است که ممکن است ردیابی/پشته خطا را از دست بدهیم و به ما اطلاع می دهد که چه چیزی و در کجا خطایی رخ داده است. نکته این است که ما نمی دانیم چه چیزی باعث خطا شده است. اکسپرس همان چیزی است که ما می نامیم خط میانی خطای خطای، دقیقاً مانند هر نوع واسطه یا درخواست کننده درخواست ، (req, res, next)=> {}بشر آنچه این را خاص می کند این است که به جای سه پارامتر دارای چهار پارامتر است: (error, req, res, next)=> {}بشر این دقیقاً مانند هر میانی سفارش سفارش است اما برای رسیدن به آن (در خط لوله یا دنباله میان افزار) باید خطایی را که در آن گرفتاریم عبور کنیم catch (error) { /* */ } به next عملکرد. مثال:

function someRequestHandler(req, res, next) {
    try {
        /* Do something */
    } catch (error) {
        return next(error);
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بنابراین اکنون ، به جای بازگشت یک پیام کلی که ، "Something went wrong please, try again later.", حتی بدون اینکه بدانیم چه اشتباهی رخ داده است ، اکنون ما یک مکانیسم فداکار برای تجزیه و بازگرداندن پاسخ خطای مناسب داریم.

امضای میانی دستی خطای ما به این شکل خواهد بود:

/* error-handling middleware */
function errorHandler(error, req, res, next) {
    /*  */
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما از instanceof اپراتور بررسی اپراتور نمونه ای است که یک شیء نمونه ای از برخی از کلاس ها است. تا کنون ما از آن آگاه هستیم TokenExpiredError وت JsonWebTokenError، که نتیجه استفاده از کتابخانه JWT است.

اکنون وقتی مطمئن نیستیم که این خطا چیست ، می توانیم یک پاسخ مناسب را بر اساس نمونه خطا و یک پیام جهانی برگردانیم. ما ممکن است بتوانیم خطا را در یک پرونده ورود به سیستم یا برخی از سرور از راه دور و غیره بنویسیم.

// require TokenExpiredError and JsonWebTokenError from jwt
const { TokenExpiredError, JsonWebTokenError } = require("jsonwebtoken");
...


/* error-handling middleware */
function errorHandler(error, req, res, next) {
    if (error instanceof TokenExpiredError) {
        return res.status(401).json({
            success: false,
            message: "Unauthorized: please log in",
 });
 }

    if (error instanceof JsonWebTokenError) {
        return res.status(401).json({
            success: false,
            message: "Unauthorized: please format your auth token",
 });
 }

    // anything else
    return res.status(500).json({
        success: false,
        message: "Internal server error, something went wrong please try again",
        // as if the same error would not occur again when the user tries again 😂
 });
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

من طرفدار تکیه بر کدهای وضعیت نیستم. من معتقدم API های من “شخص ثالث” هستند و به همین ترتیب این کدهای وضعیت را ارسال می کنند که به طور منحصر به فرد این خطاهای خاص را شناسایی می کنند ، بیش از حد است. شما می خواهید مشتری در هنگام وجود یک API خود را انجام دهد 400 پاسخ خطا یا 500 پاسخ خطا؟ برخی http مشتریان هنگام بازگشت کد وضعیت ، خطاها را پرتاب می کنند 200 یا 201بشر بنابراین با نگاهی به خودم به عنوان “شخص ثالث” ، من یک کد خطا ، کد وضعیت یا کلید دیگر را می گیرم و بلافاصله پس از آن کلید حضور می یابد ، پس از آن چیزی پیش آمد. من یک مصرف کرده ام 3 API حزب که در آن بخش موفقیت و خطا وجود دارد.

{
    "success": true,
    "statusCode": "000001",
    "error": {},
    "data": {}
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

به هر حال ، همانطور که می دانیم ، ما می توانیم این میان افزار با خطا را در هر نقطه قرار دهیم اما بهتر است زیر نقاط پایانی ثبت شده ما باشد. به عنوان یک تمرین ، کنترل کننده های خود را بپیچید ، تمام دستگیرندگان درخواست شما باید در یک بسته بندی شوند try وت catch و خطا را به عملکرد بعدی منتقل کنید. این کار را با خط میانی خطای خطا انجام ندهید. صادقانه بگویم این کار را برای کسانی که به آن احتیاج دارند انجام دهید ، آنهایی که انتظار دارید چیزی اشتباه پیش برود. برخی از کارکردها وجود دارد که من این کار را نمی کنم try and catchبشر چرا؟ کنترل کننده دارای try وت catchبشر

بنابراین همیشه ما به روزرسانی می کنیم به این شکل است:

function SomeFunction(req, res, next) {
    try {
        /*  */
    } catch (error) {
        return next(error);
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

وقتی تمام شد ، این احتمال وجود دارد که ما پاسخ خطا مانند:

{
    "success": false,
    "message": "Unauthorized: please format your auth token"
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مورد دیگر خطایی زمانی است که کاربر به نقطه پایانی که وجود ندارد برخورد کند. چگونه می توانیم از این کار برخورد کنیم و آیا خط میانی با خطای ما برای آن حساب می شود؟ نه ، خط میانی خطای خطای آن را به حساب نمی آورد.

خوب ، ایده این است ، ما به یک نقطه پایانی گفتیم ، GET http://localhost:3000/auditبشر ما هرگز یک نقطه پایانی/مسیر برای حسابرسی تعریف نکردیم. بنابراین در زنجیره ای از میانه و درخواست کنندگان درخواست کننده ، در خط لوله ، هیچ چیز این درخواست را لمس نمی کند ، بنابراین Express آن را کنترل می کند:


 lang="en">
    
         charset="utf-8" />
        </span>Error<span class="nt"/>
    <span class="nt"/>
    <span class="nt"/>
        <span class="nt"><pre/></span>Cannot GET /auditing<span class="nt"/></span></span></code></pre>
<p>    <span class="nt"/><br />
<span class="nt"/></p>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>حالت تمام صفحه را وارد کنید
    

از حالت تمام صفحه خارج شوید

Express آن را اداره کرد اما ما می خواهیم برگردیم JSON نه htmlبشر بنابراین ما یک میان افزار نهایی را اضافه می کنیم ، که فقط چیزی را مانند نقطه پایانی که یافت نشد ، باز می گرداند.

باز هم ، این کار را بعد از خط میانی با خطای ما قرار می دهد.

/* not found error-handling middleware */
function notFoundHandler(req, res, next) {
    // anything else
    return res.status(404).json({
        success: false,
        message: "Endpoint not found, kindly read our documentation",
    });
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این تابع پس از خطای دست زدن به میانی استفاده می شود.

...
/* global error handling */
app.use(errorHandler);

/* handle cannot [METHOD] some endpoint */
app.use(notFoundHandler);
...
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

و ما یک پیام JSON دریافت می کنیم.

HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 79
ETag: W/"4f-ZeAuDFW/On8o/z3qFxGFjcV9e4E"
Date: Mon, 03 Mar 2025 09:01:58 GMT
Connection: close

{
  "success": false,
  "message": "Endpoint not found, kindly read our documentation"
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

منابع

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا