Flask Vue.js全栈开发|第4章:Vue.js调用API实现用户注册/登录/退出

  • 原创
  • Madman
  • /
  • /
  • 17
  • 27893 次阅读

flask vuejs 全栈开发-min.png

Synopsis: 前端 Vue.js 如何划分组件,要动态显示 Alert 消息,父组件通过 props 给子组件传值即可。用户登录前后,导航栏上分别显示 Login 和 Logout 按钮,需要使用 store 模式维护一个共同的状态。另外,vue-router 的 beforeEach 可以指定哪些路由需要用户认证

代码已上传到 https://github.com/wangy8961/flask-vuejs-madblog/tree/v0.4 ,欢迎star

1. Vue-Router 导航

1.1 页面布局 Layout

前端的大致页面布局如下:

1 vue layout

内容区域组件包含 Alert 子组件,原因是方便父组件通过 props 给子组件传值。导航栏组件共用,在 front-end/src/App.vue 中引入

创建 front-end/src/components/Navbar.vue

<template>
  <nav class="navbar navbar-expand-lg navbar-light bg-light" style="margin-bottom: 20px;">
    <div class="container">
      <router-link to="/" class="navbar-brand">
        <img src="../assets/logo.png" width="30" height="30" class="d-inline-block align-top" alt="">
          MadBlog
      </router-link>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

  <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto mt-2 mt-lg-0">
          <li class="nav-item active">
            <router-link to="/" class="nav-link">Home <span class="sr-only">(current)</span></router-link>
          </li>
          <li class="nav-item">
            <a class="nav-link disabled" href="#">Explore</a>
          </li>
        </ul>

        <form class="form-inline navbar-left mr-auto">
          <input class="form-control mr-sm-2" type="search" placeholder="Search">
          <!-- 暂时先禁止提交,后续实现搜索再改回 type="submit" -->
          <button class="btn btn-outline-success my-2 my-sm-0" type="button">Search</button>
        </form>

        <ul class="nav navbar-nav navbar-right">          
          <li class="nav-item">
            <a class="nav-link disabled" href="#">Messages</a>
          </li>
          <li class="nav-item">
            <router-link to="/profile" class="nav-link">Profile</router-link>
          </li>
          <li class="nav-item">
            <router-link to="/login" class="nav-link">Login</router-link>
          </li>
        </ul>
      </div>
    </div>
  </nav>
</template>

<script>
export default {
  name: 'Navbar'  //this is the name of the component
}
</script>

在导航栏组件中使用类似于 <router-link to="/login" class="nav-link">Login</router-link> 来导航到登录页面

修改 front-end/src/App.vue

<template>
  <div id="app">
    <navbar></navbar>
    <router-view/>
  </div>
</template>

<script>
import Navbar from './components/Navbar'

export default {
  name: 'App',
  components: {
    navbar: Navbar
  }
}
</script>

<style>
</style>

浏览器 http://localhost:8080/#/ 将看到之前的 Pong! 按钮

1.2 Alert 组件

创建子组件 front-end/src/components/Alert.vue

<template>
  <div class="alert" role="alert" v-bind:class="'alert-' + variant">
    {{ message }}
  </div>
</template>

<script>
export default {
  props: ['variant', 'message']
}
</script>

子组件 Alert.vue 可以接收父组件传递的 variantmessage 数据,下面演示父组件如何动态传递数据给 Alert.vue

创建父组件 front-end/src/components/Home.vue

<template>
  <div class="container">
    <alert 
      v-for="(alert, index) in alerts" :key="index"
      v-if="alert.showAlert"
      v-bind:variant="alert.alertVariant"
      v-bind:message="alert.alertMessage">
    </alert>
    <button type="button" class="btn btn-primary">HomePage</button>
  </div>
</template>

<script>
import Alert from './Alert'

export default {
  name: 'Home',  //this is the name of the component
  components: {
    alert: Alert
  },
  data () {
    return {
      alerts: [
        {
          showAlert: true,
          alertVariant: 'danger',
          alertMessage: 0
        },
        {
          showAlert: true,
          alertVariant: 'info',
          alertMessage: 1
        },
        {
          showAlert: true,
          alertVariant: 'dark',
          alertMessage: 2
        }
      ]
    }
  }
}
</script>

front-end/src/components/ 目录下分别创建 Login.vue, Register.vue, Profile.vue,代码在 https://github.com/wangy8961/flask-vuejs-madblog/tree/v0.4

1.3 导航守卫 beforeEach

只有用户登录后才能访问 Home、Profile 等,需要使用 Vue-Routerrouter.beforeEach() 在每次路由前判断是否需要用户验证,关于 "导航守卫" 功能请阅读官方文档 https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

修改 front-end/src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Login from '@/components/Login'
import Register from '@/components/Register'
import Profile from '@/components/Profile'
import Ping from '@/components/Ping'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    {
      path: '/register',
      name: 'Register',
      component: Register
    },
    {
      path: '/profile',
      name: 'Profile',
      component: Profile,
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/ping',
      name: 'Ping',
      component: Ping
    }
  ]
})

router.beforeEach((to, from, next) => {
  const token = window.localStorage.getItem('token')
  if (to.matched.some(record => record.meta.requiresAuth) && (!token || token === null)) {
    next({
      path: '/login',
      query: { redirect: to.fullPath }
    })
  } else if (token && to.name == 'Login') {
    // 用户已登录,但又去访问登录页面时不让他过去
    next({
      path: from.fullPath
    })
  } else {
    next()
  }
})

export default router

现在客户端如果要访问 http://localhost:8080/#/,会被重定向到 http://localhost:8080/#/login?redirect=%2F 需要先登录验证才行

2. 用户注册

Register.vue 组件代码如下:

  • codeblind
  • nihao
  • mahongan3
  • 李姝雨
  • 无敌小东东
  • Dawn Inator
  • zhuyulin
  • ygren
  • wulvtutu
  • Lunaticsky-tql
  • Hello.殷
  • sunny
  • nickzxhfjsm
  • 雀AI2023
  • anthony
  • Team12
  • vafvfdA
  • zhangsan
  • kasbar
  • ReinXD
  • sdwfqhy
  • liuwj
  • 邱晨100
  • JT Z
未经允许不得转载: LIFE & SHARE - 王颜公子 » Flask Vue.js全栈开发|第4章:Vue.js调用API实现用户注册/登录/退出

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

17 条评论

henrybill30
henrybill30

您好,我是刚开始学习的小白,请问前端代码中用户登录的时候是通过什么验证用户名密码是否正确的,是token吗,但‘api/token’这个url不是login_required的吗?

Madman
Madman henrybill30 Author

前端通过 Basic Auth 认证,提供用户名和密码,请求后端 http://localhost:5000/api/tokens,如果用户名密码正确则发放有效 Token:

@bp.route('/tokens', methods=['POST'])
@basic_auth.login_required
def get_token():
    token = g.current_user.get_jwt()
    # 每次用户登录(即成功获取 JWT 后),更新 last_seen 时间
    g.current_user.ping()
    db.session.commit()
    return jsonify({'token': token})
henrybill30
henrybill30 Madman

好的,我尝试一下,谢谢!

皓月qhy
皓月qhy

代码有个小bug 在登录成功后,这里会存储一个madblog-token,但是在导航守卫时使用的是token,所以会导致导航守卫失效 window.localStorage.setItem('madblog-token', response.data.token) const token = window.localStorage.getItem('token')

mingkai
mingkai 皓月qhy

多谢大神指出。 我说刚开始怎么点home 以后出现登录页面,登录以后点home ,还是要登录。

把const token = window.localStorage.getItem('token') 改成 const token = window.localStorage.getItem('madblog-token') 就可以了

vow
vow

1.2小节当中的

<alert 
      v-for="(alert, index) in alerts" :key="index"
      v-if="alert.showAlert"
      v-bind:variant="alert.alertVariant"
      v-bind:message="alert.alertMessage">
</alert>

v-forv-if是不是不能同时出现呢?

mingkai
mingkai vow

我也遇到这个问题。 错误提示:

5:7 error The 'alerts' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if' vue/no-use-v-if-with-v-for

mingkai
mingkai mingkai

改成compute 方法就ok了。

六六六意啊
六六六意啊

请问您用户的登录注册尝试过中文填写吗,按照您的源码来,注册中文是可以的,也可以添加进数据库,但是对于登录而言,如果是英文字母是可以前端给后端发token请求的,但是中文请求却发不过去,报错为 Cannot read property 'status' of undefined。希望可以得到解答,谢谢!

Madman
Madman 六六六意啊 Author

使用 Flask-HTTPAuth 验证用户名、密码(app/api/auth.py):

@basic_auth.verify_password
def verify_password(username, password):
    '''用于检查用户提供的用户名和密码'''
    user = User.query.filter_by(username=username).first()
    if user is None:
        return False
    g.current_user = user
    return user.check_password(password)

如果你用 postman 测试,传入中文字符的 username 时,verify_password() 函数获取的 username 字符乱码了,所以找不到用户! 你可以不用 Flask-HTTPAuth,换其他模块

moelee
moelee

到“1.3 导航守卫 beforeEach ” 这步时报错,我应该怎么处理呢? 输入图片说明

imobs
imobs

登录页面 报错要怎么处理?Uncaught (in promise) Error: Redirected when going from "/login" to "/" via a navigation guard.

Madman
Madman imobs Author

使用package.json中vue-router相同版本号

imobs
imobs Madman

和你仓库的是一个版本"vue-router": "^3.0.1"

imobs
imobs Madman

解决了,谢谢!另外说一下,评论好像上传不了图片

Madman
Madman imobs Author

是的,目前只能使用图片链接,因为用的人少😁

专题系列