iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
0
Modern Web

30天Vue出Google SSO系列 第 21

Day 21. B2E-帳戶清單

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20201006/20118686woQi7LX3z4.jpg

今天會做兩個API

  • 帳戶清單: 包含已登入、未登入的帳戶
  • 帳戶移除: 顧名思義就是把清單中的帳戶移除

開啟後端(b2e)專案開始吧~


#帳戶清單API實作

帳戶清單可以放在前端的頭像Menu中,已登入的就呈現在最上方,其餘未登入的就呈現在中間區塊,如圖碼掉的部分xD

#Step 1

開啟 /routes/users/index.js 新增路由:

router.get('/signintokens', userCtrl.userCtrlSigninTokens);

路徑 signintokens 對應controller: userCtrlSigninTokens

註: 注意這裡用的是GET方法!!

#Step 2

開啟 /controllers/users.controller.js 加上 controller: userCtrlSigninTokens

const userCtrlSigninTokens = (req, res) => {
    userModule.userModuleSigninTokens(req.signedCookies.SigninTokens).then((moduleResult) => {
        res.send(Object.assign({ success: true }, moduleResult));
    }).catch((error) => {
        res.send(Object.assign({ success: false }, error));
    });
};

#Step 3

開啟 /modules/users.module.js
判斷Token是否存在,不存在就回傳「尚未登入」:

const userModuleSigninTokens = (signinTokens) => {
    return new Promise((resolve, reject) => {
        if (signinTokens) {
            ...
        } else {
            reject({ message: "尚未登入" });
        }
    });
};

#Step 4

Token存在則逐筆解密,解密後回傳其中的帳號和姓名及啟用狀態:

let users = signinTokens.map(function (item) {
    let user;
    jwt.verify(item.token, config.jwt.tokensecret, (error, decoded) => {
        if (error) {
            reject({ message: error });
        } else {
            user = {
                accountId: decoded.accountId,
                username: decoded.username,
                active: item.active,
            };
        }
    });
    return user;
});
resolve({ users });

用到了陣列的 map 方法,作用是將陣列內容逐筆跑並且回傳想要的結果

#Step 5

寫到這邊我發現 jwt.verify jwt解密方法在module裡面用了好多次...
為了簡化程式碼及可維護性,把它額外拉出去寫了一支function:

function jwtDecode(token, func) {
    jwt.verify(token, config.jwt.tokensecret, (error, decoded) => {
        if (error) {
            reject({ message: error });
        } else {
            func(decoded);
        }
    });
}

傳入變數:

  • token: 要解密的Token
  • func: 解密成功後要做的處理

原本的解密部分就可以改寫為:

jwtDecode(item.token, (decoded) => {
    user = {
        accountId: decoded.accountId,
        username: decoded.username,
        active: item.active,
    };
});

前面有用到 jwt.verify 的程式碼都可以一併改寫!!
日後只要改這支function就好,不用每個 jwt.verify 都改一次,增加可維護性

#結果

到這邊帳戶清單API就完成囉~
我登入了第2個帳號,可以參考POSTMAN呼叫的結果:
https://ithelp.ithome.com.tw/upload/images/20200915/201186865oeXgHRmMI.jpg


#帳戶移除API實作

移除帳戶API路徑會和帳戶清單API的路徑相同,不過方法變為 PUT 並且後方帶參數來判斷要移除的帳號

#Step 1

一樣開啟 /routes/users/index.js 新增路由:

router.put('/signintokens/:accountId', userCtrl.userCtrlSigninTokens);

路徑 signintokens 及帳號參數 :accountId 對應controller: userCtrlSigninTokens

註: 注意這裡用的是PUT方法喔!!

#Step 2

開啟 /controllers/users.controller.js
因為用到同一支controller,所以controller裡面會先判斷是用什麼方法進來的
(有其他解法的話歡迎提供我會很感謝xD)

const userCtrlSigninTokens = (req, res) => {
    switch (req.method) {
        case 'GET':
            ...
            break;
        case 'PUT':
            ...
            break;
    }
};

PUT 內容會傳入Token和要移除的帳號給module處理,module回覆處理結果後簽回cookie:

userModule.userModuleRemoveSigninToken(req.signedCookies.SigninTokens, req.params.accountId)
    .then((moduleResult) => {
        res.cookie('SigninTokens', moduleResult, { signed: true });
        res.send({ success: true });
    })
    .catch((error) => {
        res.send(Object.assign({ success: false }, error));
    });

#Step 3

開啟 /modules/users.module.js 判斷Token是否存在,不存在就回傳「尚未登入」:

const userModuleRemoveSigninToken = (signinTokens, accountId) => {
    return new Promise((resolve, reject) => {
        if (signinTokens) {
            ...
        } else {
            reject({ message: "尚未登入" });
        }
    });
};

再加一層判斷帳號有沒有傳進來,沒有就回傳「帳戶不符規定」:

if (signinTokens) {
    if (accountId) {
        ...
    } else {
        reject({ message: "帳戶不符規定" });
    }
} else {
    reject({ message: "尚未登入" });
}

#Step 4

接著會用到陣列的 findIndex 方法找到對應帳號的索引值

findIndex 作用是搜尋陣列中符合指定條件的項目索引值

let signinTokensIndex = signinTokens.findIndex(function (item) {
    let decodedAccountId;
    jwtDecode(item.token, (decoded) => {
        decodedAccountId = decoded.accountId
    });
    return decodedAccountId === accountId;
});

#Step 5

有索引值後就知道要移除的帳號是第幾筆,再使用陣列的 splice 方法來移除並回傳,找不到索引值就回傳「無此帳號」

splice 作用是移除陣列中指定的索引項目 第2個參數是要移除的筆數

if (signinTokensIndex > -1) {
    signinTokens.splice(signinTokensIndex, 1);
    resolve(signinTokens);
} else {
    reject({ message: "無此帳號" });
}

#結果

到這邊移除帳戶API就完成囉~
來看看效果吧~原本清單有兩筆,移除其中一筆再抓一次清單就剩下一筆:
gif已死QQ


今日重點:

  • 使用 GET 方法取得「帳戶清單」
  • 使用 PUT 方法做「帳戶移除」
  • 陣列方法及使用方式
    • findIndex: 搜尋陣列中符合指定條件的項目索引值
    • splice: 移除陣列中指定索引的項目

有需要改進或是任何意見建議歡迎下面留言~


上一篇
Day 20. F2E-頭像Menu
下一篇
Day 22. F2E-帳戶清單
系列文
30天Vue出Google SSO30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言