Flask前後端分離實踐:Todo App(2)
- 2019 年 11 月 30 日
- 筆記
前序文章
- Flask前後端分離實踐:Todo App(1) 使用Vue.js搭建Todo App
本文項目地址: https://github.com/frostming/flask-vue-todo
在上一篇文章里我們已經用Flask+Vue搭建了一個可以把數據持久化到伺服器的Todo App。那麼,為了讓多人一起使用這個App,我們需要對數據按用戶做隔離,這樣就自然需要一個註冊/登錄介面。在前後端分離的架構里,我們是怎麼驗證用戶,保持會話的呢?
用戶登錄
先複習一下以往用Flask是怎麼解決這問題的,沒錯,通過Flask-Login模組,從request中獲取用戶名和密碼,驗證通過後用login_user
記錄到會話中,之後的請求就會帶有登錄資訊了。如果要退出登錄,只需要調一下logout_user
就可以了。
那麼使用前後端分離以後,所有對後端的請求都是以Ajax的方式發送,上面的方法依然有效!區別僅僅在於,我們將請求改成JSON格式之後,後端是從request.get_json()
中獲取的。為此,我們專門建立一個名為auth
的藍圖:
Python
@bp.route('/login', methods=['POST']) def login(): user_data = request.get_json() form = LoginForm(data=user_data) if form.validate(): user = form.get_user() login_user(user, remember=form.remember.data) return jsonify({'status': 'success', 'user': user.to_json()}) return jsonify({'status': 'error', 'message': form.errors}), 403
後端只接收POST請求,因為GET都在前端那邊,自然也就沒有login_view的配置了。前端那邊,axios發請求時自動會帶上cookie,所以後端這邊依然可以通過flask_login.current_user
拿到當前用戶。
表單與驗證
現在我們需要一個包含表單的登錄頁面,而我們知道,所有的頁面都是前端渲染。所以這裡wtform或flask-boostrap就不太能派上用場了。好在表單也比較簡單,不是很難寫。
Html
<template> <form action="/auth/login" method="post"> <h2>Login</h2> <div class="form-group"> <label for="username">Username</label> <input type="text" id="username" name="username" v-model="username" required> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" id="password" name="password" v-model="password" required> </div> <div class="form-group"> <label for="remember"> <input type="checkbox" id="remember" name="remember" v-model="remember"> Remember Me </label> </div> <div class="form-footer"> <button type="submit" name="submit" class="btn">Submit</button> <router-link to="/" class="btn">Return Home</router-link> </div> </form> </template>
有一表單驗證的工作,比如必填項,長度限制等,完全不需要後端的,可以在前端完成。我們需要寫一個提交的函數,綁定到表單的submit動作上:
Javascript
methods: { checkForm (e) { e.preventDefault() const vm = this api.login({ username: this.username, password: this.password, remember: this.remember }).then(data => { vm.$router.push({ path: '/' }, () => { vm.success('Logged in successfully!') }) }).catch(e => { let errors = e.response.data.message for (let key in errors) { errors[key].forEach(e => {vm.error(`${key}: ${e}`)}) } }) } }
但有些驗證工作,比如密碼校驗,還是要麻煩後端的,所以這裡我們獲取後端返回的錯誤(儲存在data.message
中),然後依次渲染在頁面中(這裡我使用了一個Vue的插件Vue-flask-message來完成)。
後端驗證這一塊,由於沒有渲染需求了,可以不用wtform這一套,改用marshmallow,但為了後面的方便,我還是使用了Flask-WTF,把驗證放到表單類里。
Python
from flask_wtf import FlaskForm class LoginForm(FlaskForm): username = StringField('Username', validators=[Length(max=64)]) password = PasswordField('Password', validators=[Length(8, 16)]) remember = BooleanField('Remember Me') def validate_username(self, field): if not self.get_user(): raise ValidationError('Invalid username!') def validate_password(self, field): if not self.get_user(): return if not self.get_user().check_password(field.data): raise ValidationError('Incorrect password!') def get_user(self): return User.query.filter_by(username=self.username.data).first()
完成了登錄部分,那麼註冊介面也大同小異,總結起來,大致思想是:
- 對於無需後端的驗證,由前端完成。
- 後端的驗證,通過響應內容傳回錯誤。
- 驗證錯誤通過Vue-flash-message顯示到頁面上。
- login和register的視圖函數僅處理POST請求。