专栏首页Frost's BlogFlask前后端分离实践:Todo App(2)

Flask前后端分离实践:Todo App(2)

前序文章

本文项目地址: 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请求。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 在博客与笔记中使用Markdown

    这些我都不是特别满意,于是便萌生了干脆建一个个人博客的想法。通过多方考查,得到一个比较好的解决方案:Hexo + Github page。Github page...

    岂不美哉Frost
  • Flask前后端分离实践:Todo App(1)

    有人要问,我为什么要前后端分离?这个说起就话长了,网上也能搜索到一些解答,不过可简要概括为以下两点:

    岂不美哉Frost
  • SQLite 爬坑记

    作为从零开始的Web开发人员,在项目开发中总是遇到这样那样的坑,其中数据库的坑最多。由于在功能完善过程中需要变换频繁,不可避免地要更改DB Schema,不过我...

    岂不美哉Frost
  • 软件安全性测试(连载12)

    除了XSS注入外,还存在XML注入、JSON注入和XPath注入三种代码注入情形。

    小老鼠
  • 基于Django的电子商务网站开发(连载18)

    系统为用户提供用户登录密码的修改。根据需求定义,修改用户密码的时候,必须提供旧密码、新密码和新密码的确认,并且新密码不能与旧密码相同。如果旧密码不正确、新密码与...

    小老鼠
  • layui数据表格checkbox设置部分不可选

    在layui数据表格中设置了字段为type:checkbox 但是想要实现部分不显示,不可选的功能。

    宣言言言
  • 第03期:Prometheus 数据采集(二)

    爱可生上海研发中心成员,研发工程师,主要负责 DMP 平台监控告警功能的相关工作。

    爱可生开源社区
  • java 调用mysql存储过程

    由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面。

    ydymz
  • 深度学习中常见的损失函数(摘自我的书)

    在深度学习分类任务中,我们经常会使用到损失函数,今天我们就来总结一下深度学习中常见的损失函数。

    黄鸿波
  • Linux音频驱动-PCM设备

    pcm(Pulse-code modulation)脉冲编码调制,是将模拟信号转化为数字信号的一种方法。声音的转化的过程为,先对连续的模拟信号按照固定频率周期性...

    DragonKingZhu

扫码关注云+社区

领取腾讯云代金券