如何用Eggjs從零開始開發一個專案(3)
上一篇中我們編寫了使用者註冊登入、登入的程式碼,學習瞭如何進行使用者的認證(JWT),如何安全地儲存用的密碼(hash)。這一篇我們有以下2個任務:
- 獲取
token
中的資料; - 通過
model
來同步資料庫。
獲取token中的資料
使用者登入的時候我們已經使用者的一些基本資訊加密儲存到token
中,通過路由配置我們能簡單的去控制哪些介面需要登入,哪些介面不需要登入,但是如果再細化到使用者或者使用者角色,只是從路由層面就難以控制了,我們需要拿到使用者資訊,並將使用者資訊儲存在session
中,方便我們隨時取用。那要怎麼做呢?這裡我們需要編寫一箇中間件來實現這個功能。程式碼如下:
// app/middleware/eggJwt module.exports = options => { return async function jwt(ctx, next) { const token = ctx.request.header.authorization; if (token) { // 這裡有個坑,前端一般傳過來是Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGl... // 但是解碼時不需要Bearer,所以需要手動處理一下 const tokenStr = token.split(' ')[1]; try { // 解碼token並存儲到session中 const decode = ctx.app.jwt.verify(tokenStr, options.secret); ctx.session.user = decode; await next(); } catch (error) { ctx.status = 500; ctx.body = { message: error.message }; return; } } else { ctx.status = 401; ctx.body = { message: '使用者未登入' }; return; } }; };
這裡說明一下eggjs
的外掛機制,如果你在config.default.js
中配置:
config.middleware = ['eggJwt'];
那麼,這個外掛將作用於所有的路由,除非有特殊配置,但是這裡我們不需要,所以不加這一行程式碼。我們按需配置:
// app/router.js module.exports = app => { const { router, controller } = app; const jwt = app.middleware.eggJwt(app.config.jwt); router.get('/', controller.home.index); router.post('/createUser', jwt, controller.user.createUser); router.get('/getUsers', jwt, controller.user.getUsers); router.post('/register', controller.user.register); router.post('/login', controller.user.login); };
我們手動引入後按需配置即可。
這個時候我們去訪問配置了jwt的介面,就會執行外掛,解析token
,並將解析結果儲存到session
裡。我們修改一下user controller
的程式碼試一下:
async getUsers() { // 列印session console.log(this.ctx.session.user, '==========='); const users = await this.ctx.model.User.findAll(); this.ctx.body = { code: 200, data: users }; }
配置好token後請求localhost:7001/getUsers
,資料能正常返回,同時控制檯打印出了:
{ name: 'xiaoming', iat: 1609055716, exp: 1609062916 } ===========
可以看到這時我們就拿到了token
裡儲存的資料。
通過model來同步資料庫
按照我們現在的程式碼,如果我們需要調整model
中的欄位,我們需要修改model
檔案,然後再寫指令碼同步修改資料庫中的欄位,這樣太麻煩了。幸好,Sequelize
提供了一個方法讓我們可以根據model
去同步資料庫裡的欄位:
User.sync()
- 如果表不存在,則建立該表(如果已經存在,則不執行任何操作)User.sync({ force: true })
- 將建立表,如果表已經存在,則將其首先刪除User.sync({ alter: true })
- 這將檢查資料庫中表的當前狀態(它具有哪些列,它們的資料型別等),然後在表中進行必要的更改以使其與模型匹配.
注意我們這裡只能用User.sync({ alter: true })
,那麼要在什麼時候去執行呢?我們想在每次服務啟動時去同步資料庫欄位,同時eggjs也提供了生命週期供我們使用:
// app.js
class AppBootHook {
constructor(app) {
this.app = app;
}
configWillLoad() {
// 此時 config 檔案已經被讀取併合並,但是還並未生效
// 這是應用層修改配置的最後時機
// 注意:此函式只支援同步呼叫
}
async didLoad() {
// 所有的配置已經載入完畢
// 可以用來載入應用自定義的檔案,啟動自定義的服務
}
async willReady() {
// 所有的外掛都已啟動完畢,但是應用整體還未 ready
// 可以做一些資料初始化等操作,這些操作成功才會啟動應用
// 根據model同步資料庫
await this.app.model.sync({ alter: true });
}
async didReady() {
// 應用已經啟動完畢
}
async serverDidReady() {
// http / https server 已啟動,開始接受外部請求
// 此時可以從 app.server 拿到 server 的例項
}
}
module.exports = AppBootHook;
這時,我們在user model
中新增一個欄位:nickname: STRING(10)
,然後重啟一下服務,開啟資料庫看看user
表,果然多了一個nickname
的列。這個功能極大地方便了我們去同步model
與資料庫欄位,但是操作的時候也要小心,不要誤刪欄位。
這一篇內容不多,但是都是很實用的功能,下一篇我們將通過Sequelize
來實現表的關聯查詢。