Axios و Interceptor ها

22 بهمن 1399
Axios و Interceptor ها

Axios و Interceptor ها

مدیریت خطاها به صورت محلی بسیار مهم است و از مشکلات بسیار زیادی جلوگیری می کند اما این روش مختص به کامپوننت های مشخصی می باشد. به طور مثال اگر در کامپوننت FullPost خطایی اتفاق بیفتد، آن را در همان کامپوننت مدیریت می کنیم. این روش کاملا منطقی است؛ مثلا شما می خواهید اگر درخواست های HTTP درون کامپوننت FullPost با خطا روبرو شدند، پیام خاصی را نمایش دهید اما اگر در Add a Post اتفاق افتادند پیام دیگری نمایش دهیم.

در این میان برخی اوقات می خواهیم کدهایمان را به صورت سراسری در تمام برنامه اجرا کنیم؛ یعنی فارغ از اینکه کدام درخواست HTTP از کدام کامپوننت ارسال شده است، می خواهیم با ارسال یا دریافت هر درخواست HTTP کار خاصی انجام دهیم. ما می توانیم این کار را با کمک Axios و چیزی به نام interceptor ها انجام دهیم. به زبان ساده تر Interceptor ها (به معنی متوقف کننده یا حائل) توابعی هستند که به طور سراسری تعریف می شوند و روی هر درخواست ارسال شده و هر پاسخ دریافت شده اجرا می شوند. موارد استفاده ی interceptor ها چنین مواردی را شامل می شوند:

  • ایجاد header های مشترک (مانند authorization header ها و...)
  • log کردن پاسخ های سرور (response)
  • مدیریت خطاها به صورت سراسری (global)

هشدار: در جلسه ی قبل به صورت عمدی url سرور تمرینی را با اضافه کردن چند s در انتهای آن خراب کردیم تا بتوانیم خطای ایجاد شده را مدیریت کنیم (فایل Blog.js درون componentDidMount). قبل از ادامه ی این جلسه آدرس url را تصحیح کرده و به حالت قبل برگردانید:

axios.get('https://jsonplaceholder.typicode.com/posts')

برای شروع کار وارد فایل index.js شوید چرا که این فایل، سراسری ترین فایلی است که در برنامه ی react ما موجود است:

ReactDOM.render( <App />, document.getElementById( 'root' ) );

همانطور که می بینید در این فایل کامپوننت پدر (اصلی) تمام برنامه، یعنی کامپوننت App، را به DOM متصل کرده ایم و باقی کار ها از اینجا انجام می شود بنابراین بهتر است Axios را در همینجا import کنیم:

import axios from 'axios';

حالا اگر در فایل index.js از interceptor ها استفاده کنیم، تمام درخواست های ارسالی و دریافتی تحت تاثیر قرار خواهند گرفت چرا که در بالاترین سطح سراسری برنامه ی خود قرار گرفته ایم. برای ساخت یک interceptor جدید به شکل زیر عمل می کنیم:

axios.interceptors.request.use();

با این کار یک interceptor جدید خواهیم داشت. متد use که در آخر این زنجیره قرار دارد یک تابع را به عنوان پارامتر ورودی خود قبول می کند و یک config یا همان request را نیز به طور خودکار به این تابع پاس میدهد. در ابتدای کار برای آنکه بهتر متوجه عملکرد آن شویم فقط این config یا request را در کنسول مرورگر log می کنیم:

axios.interceptors.request.use(request => {
    console.log(request);
});

همانطور که مشاهده می کنید request به طور خودکار به تابع پاس داده می شود و نیازی نیست شما کار خاصی انجام بدهید. حالا که request را در کنسول مرورگر چاپ کرده ایم باید به مرورگر سر بزنیم و نتیجه را ببینیم. با کلید f12 در مرورگر خود (کروم یا فایرفاکس) قسمت dev tools باز خواهد شد. از صفحه ی باز شده سربرگ console را انتخاب کرده و مرورگر خود را refresh کنید (توجه داشته باشید که مثل همیشه باید از قبل سرور مجازی را با دستور npm start اجرا کرده باشید).

Interceptor در قسمت کنسول مرورگر
Interceptor در قسمت کنسول مرورگر

همانطور که می دانید با refresh کردن صفحه از سرور تمرینی خود عناوین پست ها را دریافت می کنیم (ر.ک به جلسات قبل) و شیء ای که در قسمت کنسول مرورگر مشاهده می کنید پیکربندی این درخواست ما است؛ header های درخواست، نوع متد (GET یا POST) و همه چیز را می توانید در این شیء ببینید. احتمالا شما هم متوجه شده اید که به جای عناوین پست ها، پاراگراف something went wrong به ما نمایش داده می شود. ما در قسمت قبل این پاراگراف را به همراه کدهای دیگرش طراحی کرده بودیم تا در صورت بروز خطا نمایش داده شود. به نظر شما چرا در حال حاضر نمایش داده شده است؟! به دلیل اینکه ما درخواست را بلوکه (متوقف) کرده ایم!

شما وظیفه دارید که در هر بار استفاده از interceptor ها، شیء config یا request را return کنید تا جلوی درخواست های مختلف برنامه گرفته نشود، در غیر این صورت باعث بلوکه شدن تمام درخواست ها خواهید شد. بنابراین برای حل این مشکل می گوییم:

axios.interceptors.request.use(request => {
    console.log(request);
    return request;
});

حالا مرورگر را دوباره refresh کنید و می بینید که مشکل حل شده است و همچنین با ایجاد درخواست های جدید (مثلا کلیک روی عنوان پست ها برای باز کردنشان در FullPost) یک interceptor جدید در قسمت کنسول مرگرور مشاهده می کنیم. همانطور که گفتیم interceptor ها تمام درخواست ها را در تمام کامپوننت ها تحت تاثیر قرار می دهند اما هدف اصلی interceptor ها چیز دیگری است؛ ما می توانیم قبل از return کردن request، آن را ویرایش کنیم (مثلا header های خودمان را اضافه کنیم و...).

دوباره به کدی که برای interceptor نوشته بودیم دقت کنید. ما گفتیم که تابع use یک تابع را به عنوان پارامتر ورودی دریافت می کند اما پارامتر دومی نیز میگیرد! پارامتر دوم نیز یک تابع است که برای مدیریت خطاها نوشته می شود و شی ای به نام error به صورت خودکار به این تابع پاس داده خواهد شد. من این پارامتر را نیز اضافه می کنم و خطاهایمان را در قسمت کنسول log می کنم:

axios.interceptors.request.use(request => {
    console.log(request);
    return request;
}, error => {
    console.log(error);
});

در این حالت خطا را بلوکه کرده ایم! بله خطاها نیز باید مانند خود درخواست ها return شوند تا درون interceptor ها بلوکه (متوقف) نشوند.

سوال: مگر چه عیبی دارد که خطاها بلوکه شوند؟ آیا به خطاها نیاز داریم؟

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

بنابراین برای حل این مشکل می گوییم:

axios.interceptors.request.use(request => {
    console.log(request);
    return request;
}, error => {
    console.log(error);
    return Promise.reject(error);
});

برای شبیه سازی خطا و تست این کد به Blog.js رفته و url سرور تمرینی را مانند جلسه ی قبل خراب کنید:

axios.get('https://jsonplaceholder.typicode.com/postssss')

احتمالا انتظار دارید که خطا را در کنسول مرورگر ببینید اما اینطور نیست! error ای که در کد بالا console.log کردیم مربوط به «خطاهای هنگام ارسال درخواست» است (مثل زمانی که اینترنت شما قطع شده باشد و نتوانید درخواست خود را از برنامه تان ارسال کنید).

ما می توانیم یک interceptor برای پاسخ ها نیز بسازیم:

axios.interceptors.response.use();

اگر دقت کنید متوجه می شوید که به جای request از شیء response استفاده کرده ایم. بدین ترتیب این interceptor برای پاسخ ها خواهد بود. حالا کدها را تکمیل می کنیم:

axios.interceptors.request.use(request => {
    console.log(request);
    return request;
}, error => {
    console.log(error);
    return Promise.reject(error);
});

axios.interceptors.response.use(response => {
    console.log(response);
    return response;
}, error => {
    console.log(error);
    return Promise.reject(error);
});

interceptor اول مخصوص درخواست های ارسالی است و interceptor دوم مخصوص پاسخ های دریافتی است. همانطور که می بینید در interceptor دوم به جای request از response استفاده کرده ایم. حالا اگر به مرورگر برویم:

خطای گرفته شده از interceptor
خطای گرفته شده از interceptor

همانگونه که مشاهده می کنید پیام خطا نمایش داده می شود یعنی هم به صورت محلی خطا را مدیریت کرده ایم و هم از طریق interceptor دوم که نوشته بودیم. حالا می توانید به Blog.js رفته و url سرور تمرینی را به حالت صحیح برگردانید:

axios.get('https://jsonplaceholder.typicode.com/posts')

سپس دوباره به مرورگر می رویم:

دو interceptor مجزا در قسمت console مرورگر
دو interceptor مجزا در قسمت console مرورگر

همانطور که مشاهده می کنید دو شیء در کنسول مرورگر وجود خواهد داشت که هر کدام مربوط به یکی از interceptor های ما است؛ شیء اول مربوط به interceptor اول (ارسال درخواست) و شیء دوم مربوط به interceptor دوم (دریافت پاسخ) می باشد. برای مطمئن شدن می توانید شیء دوم را باز کنید و  متوجه می شوید که در قسمت Data، تمام پست های ارسالی از سمت سرور تمرینی قرار دارند.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

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

حسن
04 دی 1402
این بلاگ از همه ویدیوهای اموزشی برام مفید تر بود

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

Reza Bozorgi
16 مهر 1399
ضمن تشکر از شما، در قسمت قبلی که با شرطی روی هوکز componentDidUpdate در FullPost.js اومده بودید جلوی Infinite Loop را گرفته بودید من بجای آن از shouldComponentUpdate استفاده کردم و تا قبل از Interceptorsهای Axios به‌درستی کار میکرد. اما با فعال کردن قسمت response.use اینترستور اکسویس دوباره به لوپ افتاد و مجبور به گذاشتن شروط شدم. shouldComponentUpdate(nextProps, nextState) { return ( nextProps.id !== this.props.id || nextState.fullPost !== this.state.fullPost ); } علت آنرا متوجه نشدم چی میتونه باشد.

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