مدیریت خطاها به صورت محلی بسیار مهم است و از مشکلات بسیار زیادی جلوگیری می کند اما این روش مختص به کامپوننت های مشخصی می باشد. به طور مثال اگر در کامپوننت FullPost خطایی اتفاق بیفتد، آن را در همان کامپوننت مدیریت می کنیم. این روش کاملا منطقی است؛ مثلا شما می خواهید اگر درخواست های HTTP درون کامپوننت FullPost با خطا روبرو شدند، پیام خاصی را نمایش دهید اما اگر در Add a Post اتفاق افتادند پیام دیگری نمایش دهیم.
در این میان برخی اوقات می خواهیم کدهایمان را به صورت سراسری در تمام برنامه اجرا کنیم؛ یعنی فارغ از اینکه کدام درخواست HTTP از کدام کامپوننت ارسال شده است، می خواهیم با ارسال یا دریافت هر درخواست HTTP کار خاصی انجام دهیم. ما می توانیم این کار را با کمک Axios و چیزی به نام interceptor ها انجام دهیم. به زبان ساده تر Interceptor ها (به معنی متوقف کننده یا حائل) توابعی هستند که به طور سراسری تعریف می شوند و روی هر درخواست ارسال شده و هر پاسخ دریافت شده اجرا می شوند. موارد استفاده ی interceptor ها چنین مواردی را شامل می شوند:
هشدار: در جلسه ی قبل به صورت عمدی 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 اجرا کرده باشید).
همانطور که می دانید با 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 دوم که نوشته بودیم. حالا می توانید به Blog.js رفته و url سرور تمرینی را به حالت صحیح برگردانید:
axios.get('https://jsonplaceholder.typicode.com/posts')
سپس دوباره به مرورگر می رویم:
همانطور که مشاهده می کنید دو شیء در کنسول مرورگر وجود خواهد داشت که هر کدام مربوط به یکی از interceptor های ما است؛ شیء اول مربوط به interceptor اول (ارسال درخواست) و شیء دوم مربوط به interceptor دوم (دریافت پاسخ) می باشد. برای مطمئن شدن می توانید شیء دوم را باز کنید و متوجه می شوید که در قسمت Data، تمام پست های ارسالی از سمت سرور تمرینی قرار دارند.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.