فصل پیوست ۲: پیوست توکن به درخواست‌های خروجی

Attaching Tokens to Output Requests

Vue.JS 2 - فصل پیوست 2: پیوست توکن به درخواست های خروجی - قسمت 122

در قسمت قبل مقدمات اتصال توکن به درخواست های خروجی را پیاده سازی کردیم و حالا نوبت به عملیاتی کردن آن است اما قبل از رفتن به سراغ اصل مطلب باید کدها را کمی ویرایش کنیم. در جلسه قبل سوالی را مطرح کردیم: در حال حاضر اگر به صفحه Signup رفته و با یک ایمیل جدید یک حساب کاربری جدید ایجاد کنید، در کنسول مرورگر خطا دریافت می کنید. چرا؟ در قدم اول به دلیل اینکه هنوز توکن خود را به همراه درخواست ارسال نمی کنیم و firebase به ما مجوز دسترسی نمی دهد. قبل از حل این مشکل باید چند کار را انجام بدهیم چرا که پیوست نشدن توکن به درخواست تنها خطای ما نیست. در ابتدا یک خصوصیت دیگر به State اضافه می کنیم که حاوی اطلاعات کاربر اول ما خواهد بود:

state: {
  idToken: null,
  userId: null,
  user: null
},

سپس به جای اینکه در fetchUser کاربر اول را بگیریم و روی خصوصیت نداشته email تنظیم کنیم، کاربر اول را در user در State ذخیره می کنیم:

fetchUser({ commit }) {
  globalAxios
    .get("/users.json")
    .then(res => {
      console.log(res);
      const users = [];
      const data = res.data;
      for (let key in data) {
        const user = data[key];
        user.id = key;
        users.push(user);
      }
      console.log(users);
      commit('storeUser', users[0]);
    })
    .catch(error => console.log(error));
}

همانطور که می بینید در انتهای این action، یک mutation را commit کرده ام که هنوز وجود ندارد (storeUser) و کاربر اول را به آن پاس داده ام. اگر یادتان باشد قبلا به جای commit کردن این کاربر، به شکل زیر عمل می کردیم:

this.email = users[0].email;

طبیعتا در این فایل (Store.js) خصوصیتی به نام email نداریم که بخواهیم ایمیل کاربر را در آن ذخیره کنیم بنابراین این کد باید عوض می شد تا به خطا نخوریم. در مرحله بعد باید mutation بالا را تعریف کنیم بنابراین به قسمت mutation ها رفته و می گوییم:

mutations: {
  authUser(state, userData) {
    state.idToken = userData.token;
    state.userId = userData.userId;
  },
  storeUser(state, user) {
    state.user = user;
  }
},

تنها کاری که این mutation انجام می دهد، اضافه کردن user به خصوصیت user در state است. در مرحله بعد باید یک Getter را تعریف کنیم تا کاربر ذخیره شده در this.state را دریافت کرده و در صفحات مورد نظر نمایش دهیم:

getters: {
  user(state) {
    return state.user;
  }
}

این getter نیز بسیار ساده است و نیازی به توضیح ندارد. تنها کاری که انجام می دهد، برگرداندن خصوصیت user در state است. حالا وقت آن رسیده که به Dashboard.vue برگردیم و به جای data از یک خصوصیت computed برای نمایش کاربر استفاده کنیم:

export default {
  computed: {
    email() {
      return this.$store.getters.user.email;
    }
  },
  created() {}
};
</script>

با این کار می توانیم ایمیل کاربر را دریافت کرده و به عنوان خصوصیتی به نام email از آن استفاده کنیم. در قدم بعدی باید fetchUser را در created صدا بزنم چرا که در حال حاضر created کاملا خالی است. بنابراین:

<script>
import axios from "axios";

export default {
  computed: {
    email() {
      return this.$store.getters.user.email;
    }
  },
  created() {
    this.$store.dispatch("fetchUser");
  }
};
</script>

آیا حالا کدهای ما کار می کند؟ خیر! هنوز توکن را به درخواست پیوست نکرده ایم. برای این کار ابتدا به اکشن storeUser می رویم که مسئول ثبت داده های کاربران در پایگاه داده ما است. عملیات read ما نیاز به آن توکن دارد بنابراین می توانیم علاوه بر commit، کل state را نیز در آن دریافت کنیم و توکن را از state بگیریم:

storeUser({ commit, state }, userData) {
  if (!state.idToken) {
    return;
  }
  globalAxios.post('/users.json', userData)
    .then(res => console.log(res))
    .catch(error => console.log(error))
},

در اینجا گفته ام اگر idToken تعریف نشده بود (توکن امنیتی را نداشتیم) فقط return کن که در عمل یعنی هیچ کاری نکن تا از این متد خارج شویم. در غیر این صورت باید درخواست خود را ارسال کنیم. حالا برای پاس دادن توکن باید یک query string ساده را به شکل زیر به Firebase ارسال کنیم:

storeUser({ commit, state }, userData) {
  if (!state.idToken) {
    return;
  }
  globalAxios.post('/users.json' + '?auth=' + state.idToken, userData)
    .then(res => console.log(res))
    .catch(error => console.log(error))
},

همانطور که می بینید ساختار پاس دادن توکن امنیتی در firebase به شکل بالا است؛ باید یک query parameter به نام auth را به انتهای URL خود بچسبانید و توکن را به آن بدهید. این تنها کاری است که باید انجام داد. حالا همین کار را برای fetchUser نیز انجام می دهم:

fetchUser({ commit }) {
  if (!state.idToken) {
    return;
  }
  globalAxios.get('/users.json' + '?auth=' + state.idToken)
    .then(res => {
      console.log(res);
      const users = [];
      const data = res.data;
// بقیه کدها //

با این کار کدهای ما تصحیح شده است. در حال حاضر به مرورگر می رویم و با یک ایمیل جدید ثبت نام می کنیم. با کلیک روی submit، در قسمت کنسول مرورگر سه log مختلف چاپ می شود:

  • log اول را خودمان نوشته بودیم که فقط اطلاعات تایپ شده در فرم را نمایش می داد.
  • log دوم، پاسخ اولیه Firebase مبنی بر دریافت درخواست شما است. در این پیام statusText خالی است.
  • log سوم، پاسخ نهایی Firebase برای ثبت موفقیت آمیز درخواست و تایید کامل آن است. در این پیام statusText برابر OK می باشد.
هر سه دستور log مختلف مربوط به فرم و فرآیند login در اینجا دیده می شود
هر سه دستور log مختلف مربوط به فرم و فرآیند login در اینجا دیده می شود

شما می توانید هر سه دستور log را در تصویر بالا مشاهده کنید. حالا اگر به پایگاه داده خود در مرورگر بروید، می بینید که کاربر جدید در آنجا هم ثبت شده است در حالی که ثبت نام های قبلی باعث ثبت هیچ کاربری نشده اند:

کاربر با موفقیت در پایگاه داده ثبت شده است و داده های او نیز در پایگاه داده ی خودمان موجود است
کاربر با موفقیت در پایگاه داده ثبت شده است و داده های او نیز در پایگاه داده خودمان موجود است

البته هنوز در فایل dashboard.vue به مشکل می خوریم که چند دلیل دارد. ابتدا باید خصوصیت email را طوری تنظیم کنیم که اگر وجود داشت، نمایش داده شود نه اینکه بدون توجه به آن، بخواهیم مقدارش را نمایش بدهیم. منظور من این قسمت است:

<p>Your email address: {{ email }}</p>

برای حل این مشکل می توانیم در خصوصیت email بگوییم که اگر user تعریف نشده است، مقدار False را و در غیر این صورت، مقدار email را برگردان:

computed: {
  email() {
    return !this.$store.getters.user ? false : this.$store.getters.user.email;
  }
},

توجه کنید که حتما علامت ! را قرار بدهید تا مشخص کنید که اگر user تعریف نشده باشد، false را برگردانیم. این مشکل حل شد اما مشکل بعدی ما در فایل store.js است. در این فایل و در قسمت fetchUser یادمان رفته است که state را نیز به همراه commit بگیریم:

fetchUser({ commit, state }) {
  if (!state.idToken) {
    return;
  }
  globalAxios.get('/users.json' + '?auth=' + state.idToken)
// بقیه کدها //

ما در اینجا از state.idToken استفاده کرده بودیم اما state را از شیء context خارج نکرده بودیم بنابراین خطا می گرفتیم. حالا که state را نیز خارج کرده ایم (کد بالا) می توانیم به state.idToken دسترسی داشته باشیم. در حال حاضر اگر به مرورگر بروید، باز هم در صفحه dashboard چیزی به جز false نمی بینید. چرا؟ به دلیل اینکه login نشده ایم! بنابراین اول به صفحه login رفته و با یکی از ایمیل ها login شوید، سپس به صفحه dashboard بروید. حالا ایمیل اولین کاربر را مشاهده خواهید کرد:

Your email address: roxo@info.ir

البته حدود یک ثانیه طول می کشد که ایمیل از firebase دریافت شود بنابراین در لحظه اول false را می بینید. برای حل این مشکل می توانیم از v-if در فایل dashboard.vue استفاده کنیم:

<p v-if="email">Your email address: {{ email }}</p>

حالا تا زمانی که ایمیل load نشده باشد، اصلا این پاراگراف دیده نمی شود.

ما در این جلسه برای پاس دادن توکن از query parameter ها استفاده کردیم که به انتهای URL می چسبند اما ممکن است در backend های دیگر از روش های دیگری مانند متصل کردن توکن به header درخواست استفاده شود. در قسمت های بعدی روی بهینه تر کردن برنامه کار خواهیم کرد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش رایگان Vue js از صفر تا صد توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

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