react&flask项目实战——登录

28 篇文章 1 订阅
12 篇文章 0 订阅

引言

本来想一口吃成个胖子,但发现埋头学的效果不佳(其实是学不会。。。redux、antdpro都看不懂。。。不得不感慨一句:学历如天谴那)。于是决定先用已有的知识体系把项目搭起来再说,边搭边学吧。另外,本人虽是python出身,也学过一定时间的基础知识,并有实际全职码python代码的工作经历,但对于flask框架其实也是一知半解,很多知识点并未深入,也得边写边学。第一次练习的项目,肯定有很多问题,欢迎指正。

react

antd

antd的配置和使用必须参照官网,因为antd在不断迭代中,关键是不像下兼容,不按最新的配置方式来,用不了,所以必须严格按官网来。这里就不赘述了

api

现在最流行的调用后端接口使用的库就是axiox,但我们需要对其做一下简单封装,这样能更好的排查问题以及更好的使用。

ajax.js

import axios from 'axios'
import {message} from 'antd'

export default function ajax(url, data = {}, type = 'GET') {

    return new Promise((resolve, reject) => {
        let promise
        // 1. 执行异步ajax请求
        if (type === 'GET') { // 发GET请求
            promise = axios.get(url, { // 配置对象
                params: data // 指定请求参数
            })
        } else { // 发POST请求
            promise = axios.post(url, data)
        }
        // 2. 如果成功了, 调用resolve(value)
        promise.then(response => {
            resolve(response.data)
        // 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
        }).catch(error => {
            message.error('请求出错了: ' + error.message)
        })
    })
}

index.js

/* 接口配置 */
import ajax from './ajax'

const BASE = '/api'

export const reqLogin = (username, password) => ajax(BASE + '/login', {username, password}, 'POST')

以后所有的后端接口,都放在该index中进行配置即可

router

src/setupProxy.js

代理的配置这里就不赘述,直接分享代码

const proxy = require('http-proxy-middleware')

module.exports = function (app) {
    app.use(
        proxy('/api', {
            target: 'http://localhost:5000',
            changeOrigin: true,
            pathRewrite: {'^/api': ''}
        })
    )
}

App.js

import React, {Component} from 'react';
import './App.less'
import Login from "./pages/user/Login";
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Welcome from "./pages/Welcome";

class App extends Component {
    render() {
        return (
            <BrowserRouter>
                <Switch>
                    <Route path='/login' component={Login}/>
                    <Route path='/' component={Welcome}/>
                </Switch>
            </BrowserRouter>
        );
    }
}

export default App;

pages/user/Login/index.jsx

import {Button, Checkbox, Form, Input, message} from 'antd';
import {LockOutlined, UserOutlined} from '@ant-design/icons';
import './index.less';

import React, {Component} from 'react';
import {Redirect} from "react-router-dom";
import {reqLogin} from "../../../api";
import memoryUtils from "../../../utils/memoryUtils";
import storageUtils from "../../../utils/storageUtils";


class Login extends Component {

    onFinish = (values) => {
        // 请求登陆
        const {username, password} = values
        reqLogin(username, password).then(result => {
            console.log(result);
            if (result.status === 1) { // 登陆成功
                // 提示登陆成功
                message.success('登陆成功')
                // 保存user
                const user = result.data;
                memoryUtils.user = user // 保存在内存中
                storageUtils.saveUser(user) // 保存到local中

                // 跳转到管理界面 (不需要再回退回到登陆)
                this.props.history.replace('/')

            } else { // 登陆失败
                // 提示错误信息
                message.error(result.error)
            }
        })

    }

    render() {

        // 如果用户已经登陆, 自动跳转到管理界面
        const user = memoryUtils.user
        if (user && user._id) {
            return <Redirect to='/'/>
        }

        return (
            <div className="container">
                <div className="content">
                    <div className="top">
                        <div className="header">
                            <span className="title">smallfish platform</span>
                        </div>
                        <div className="desc">苏州太湖新城最酷的测试平台</div>
                    </div>
                    <div className="main">
                        <Form
                            name="normal_login"
                            className="login-form"
                            initialValues={{
                                remember: true,
                            }}
                            onFinish={this.onFinish}
                        >
                            <Form.Item
                                name="username"
                                rules={[
                                    {min: 4, message: '用户名至少4位'},
                                    {max: 20, message: '用户名最多20位'},
                                    {pattern: /^[a-zA-Z0-9_]+$/, message: '用户名必须是英文、数字或下划线组成'},
                                    {required: true, whitespace: true, message: '请输入用户名!'},
                                ]}
                            >
                                <Input prefix={<UserOutlined className="site-form-item-icon"/>} placeholder="Username"
                                       allowClear={true}/>
                            </Form.Item>
                            <Form.Item
                                name="password"
                                rules={[
                                    {
                                        required: true,
                                        message: '请输入密码!',
                                    },
                                ]}
                            >
                                <Input.Password
                                    prefix={<LockOutlined className="site-form-item-icon"/>}
                                    type="password"
                                    placeholder="Password"
                                />
                            </Form.Item>
                            <Form.Item>
                                <Form.Item name="remember" valuePropName="checked" noStyle>
                                    <Checkbox>记住密码</Checkbox>
                                </Form.Item>
                            </Form.Item>
                            <Form.Item>
                                <Button type="primary" htmlType="submit" className="login-form-button">
                                    登录
                                </Button>
                            </Form.Item>
                        </Form>
                    </div>
                </div>
            </div>
        );
    }
}

export default Login;

这个看着代码很多,其实登录组件是选用的antd组件库里的,可以根据自己的实际需求进行选配。

还是那句,antd再不断迭代中,V3/V4和现在V5使用的方法都在发生变更(其实如果你也是python出身,这个痛点是一直伴随着我们的,我也想说一声【坑那】)

现在antd表单提交的方法是onFinish,入参是表单提交的object

小知识点:message是antd内置的提示框,挺好用的


因为没学会redux,但我们是需要对用户信息进行保存的,要不然无法把控住用户的登录状态,所以自建cache管理仓库(哈哈哈,吹牛的啦,就是把一些关键信息进行本地保存,毕竟我们做的是小系统,自己知道存在哪儿,到时能取到即可)

所以,新建utils目录,下面先来两个对象保存变量

utils/memoryUtils.js

/*
用来在内存保存一些数据的工具模块
 */
const memoryUtils = {
    user: {}, // 保存当前登陆的user
}
export default memoryUtils;

utils/storageUtils.js

/*进行local数据存储管理的工具模块*/
import store from 'store'

const USER_KEY = 'user_key'
const storageUtils = {
    /*保存user*/
    saveUser(user) {
        // localStorage.setItem(USER_KEY, JSON.stringify(user))
        store.set(USER_KEY, user)
    },

    /*读取user*/
    getUser() {
        // return JSON.parse(localStorage.getItem(USER_KEY) || '{}')
        return store.get(USER_KEY) || {}
    },

    /*删除user*/
    removeUser() {
        // localStorage.removeItem(USER_KEY)
        store.remove(USER_KEY)
    }
}

export default storageUtils;

flask

config

basicConfig.py

import os

# 项目的绝对路径
PROJECTPATH = os.path.abspath('smallfish_platform')

配置肯定少不了,项目的路径先配置一下,后面再慢慢加

mysqlConfig.py

from properties.commonVar import LOCAL


def getMysqlConfig(env: object = LOCAL, serverName: object = '') -> object:
    # 脚本运行依赖的数据库配置
    if env == LOCAL:
        db_config = {
            'host': '127.0.0.1',
            'user': 'root',
            'passwd': '12345678',
            'db': 'db_platform',
            "port": 3306,
            'charset': 'utf8'
        }
        return db_config

数据库不可能使用一个,LOCAL这个是我们运行环境的数据库,但作为平台,我们测试对象的数据库我们也是需要进行链接的,所以先写了个模型出来,后面可以追加配置

properties

配置的各种变量参数,放置在这个里面

commonVar.py

# 数据库服务器标识
LOCAL = 'run'

models

数据库的使用,当然可以直接写sql语句,不说low不low吧,效率极低,不信你可以进行测试,所以选用flask自带的flask_sqlalchemy

database.py

将mysql与app进行绑定

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

from config.mysqlConfig import getMysqlConfig
from properties.commonVar import LOCAL

app = Flask(__name__)
db_config_local = getMysqlConfig(LOCAL)

db_local = f"mysql+pymysql://{db_config_local['user']}:{db_config_local['passwd']}@{db_config_local['host']}:{db_config_local['port']}/{db_config_local['db']}?charset=utf8"

SQLALCHEMY_BINDS = {
    LOCAL: db_local
}

# 配置默认数据库
app.config['SQLALCHEMY_DATABASE_URI'] = db_local
# 绑定多个数据库
app.config['SQLALCHEMY_BINDS'] = SQLALCHEMY_BINDS
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 将数据库绑定在flask,这样就可以直接通过flask对数据库进行操作
db = SQLAlchemy(app)

local/db_platform.py

import hashlib

from models.database import db
from properties.commonVar import LOCAL


class TbUser(db.Model):
    __tablename__ = 'tb_user'
    __bind_key__ = LOCAL
    __table_args__ = {'comment': '用户基础表'}

    id = db.Column(db.INTEGER, primary_key=True, comment='唯一ID')
    login_name = db.Column(db.String(255), nullable=False, comment='登录用户名')
    password = db.Column(db.String(64), nullable=False, comment='登录密码')
    status = db.Column(db.INTEGER, nullable=False, server_default=db.text("'1'"), comment='用户状态-0无效1有效')
    token = db.Column(db.String(64), nullable=False, comment='用户token')
    last_login_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"),
                                comment='最后登录时间')
    failure_time = db.Column(db.INTEGER, nullable=False, server_default=db.text("'0'"), comment='失效时间-时间戳')
    create_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"), comment='创建时间')
    update_time = db.Column(db.DateTime, nullable=False,
                            server_default=db.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
                            comment='修改时间')

    def check_password(self, password):
        m = hashlib.md5()
        b = password.encode(encoding='utf-8')
        m.update(b)
        str_md5 = m.hexdigest()
        if self.password == str_md5:
            return True
        else:
            return False


class TbUserInfo(db.Model):
    __tablename__ = 'tb_user_info'
    __bind_key__ = LOCAL
    __table_args__ = {'comment': '用户信息表'}

    id = db.Column(db.INTEGER, primary_key=True, comment='唯一ID')
    user_id = db.Column(db.INTEGER, nullable=False, server_default=db.text("'0'"), comment='用户唯一标识')
    user_name = db.Column(db.String(64), nullable=False, comment='用户姓名')
    user_role = db.Column(db.String(64), nullable=False, comment='用户岗位')
    user_desc = db.Column(db.String(1024), nullable=False, comment='用户详情')
    create_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"), comment='创建时间')
    update_time = db.Column(db.DateTime, nullable=False,
                            server_default=db.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
                            comment='修改时间')


if __name__ == '__main__':
    # 验证过,执行多次并不会删除已有数据,可以批量执行
    # db.create_all(bind=[LOCAL])
    password = '123456'
    m = hashlib.md5()
    b = password.encode(encoding='utf-8')
    m.update(b)
    str_md5 = m.hexdigest()
    print(str_md5)

这个就是创建了两张user表,password做了加密处理

services

这里就是对外的接口了

login.py

from flask import jsonify, request
from sqlalchemy import and_

from models.database import app
from models.local.db_platform import TbUser


@app.route('/login', methods=['POST'])
def login():
    if not request.json or 'username' not in request.json or 'password' not in request.json:
        return jsonify({"status": 0, "error": "入参错误"})
    username: str = request.json.get("username").strip()
    password: str = request.json.get("password").strip()
    users: list = TbUser.query.filter(
        and_(TbUser.login_name == username, TbUser.status == 1)).all()
    if len(users) == 0:
        return jsonify({"status": 0, "error": "没有该用户"})
    elif len(users) > 1:
        return jsonify({"status": 0, "error": f"用户:{username},重名了"})
    user: TbUser = users[0]
    if user.check_password(password):
        return jsonify({"status": 1})
    else:
        return jsonify({"status": 0, "error": "密码错误"})


if __name__ == '__main__':
    app.run(debug=True)
    # login()

flask-sqlalchemy这个库是依赖于sqlalchemy库的,所以也得安装sqlalchemy

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值