在5分钟内完成管理。 前端-react-admin,后端-Flask-RESTful



如果您需要一个快速的管理面板 ,前端是react-admin ,后端是Flask-RESTful api ,那么最少的代码是以下几行来实现。

后端Flask-RESTful API


代码本身包含一个main.py文件:

from flask import Flask, request from flask_restful import Resource, Api from flask_jwt_extended import JWTManager from flask_jwt_extended import create_access_token, jwt_required from flask_cors import CORS app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'my_cool_secret' jwt = JWTManager(app) CORS(app) api = Api(app) class UserLogin(Resource): def post(self): username = request.get_json()['username'] password = request.get_json()['password'] if username == 'admin' and password == 'habr': access_token = create_access_token(identity={ 'role': 'admin', }, expires_delta=False) result = {'token': access_token} return result return {'error': 'Invalid username and password'} class ProtectArea(Resource): @jwt_required def get(self): return {'answer': 42} api.add_resource(UserLogin, '/api/login/') api.add_resource(ProtectArea, '/api/protect-area/') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') 

让我们来看一下代码:

  • 我们的后端将仅通过RESTful api进行与外界的所有交互,即使管理面板中的授权也通过它。 为此,flask具有一个方便的模块: Flask-RESTful API
  • flask_jwt_extended模块将帮助我们保护只有经过授权才能访问的那些路由。 这里没有什么神圣的,只是将一个jwt令牌( JSON Web令牌 )添加到每个http请求中,通过该请求,我们的应用程序将了解用户已获得授权。
    上面的代码显示@jwt_required装饰器用于此目的。 您可以将其添加到应受保护的API路由中。
  • 没有flask_cors,我们将收到以下错误:
    Access to XMLHttpRequest at 'http://localhost:5000/api/login/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    在此处阅读有关CORS的更多信息。

我们放置了所有必需的库,并使用以下命令运行代码:

 python main.py 

如您所见,我为管理面板硬编码了用户名和密码:admin / habr。

在flask启动之后,您可以使用curl来测试其功能:

 curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "habr"}' localhost:5000/api/login/ 

如果出现这样的结果:

 { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIU...." } 

因此,一切都正确,您可以移至最前面。

前端react-admin


我喜欢react-admin。 这是文档 ,这是演示版本:
https://marmelab.com/react-admin-demo/#/login
登录:演示
密码:演示

为了获得与演示中相同的管理面板,我们执行以下命令:

 git clone https://github.com/marmelab/react-admin.git && cd react-admin && make install yarn add axios make build make run-demo 

现在,我们需要教她如何与后端交互。

为此,请使用以下代码替换admin/examples/demo/src/authProvider.js的内容,该代码将负责授权,离开管理面板,依此类推:

管理员/示例/演示/ src / authProvider.js
 import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK, AUTH_GET_PERMISSIONS } from 'react-admin'; import axios from 'axios'; import decodeJwt from 'jwt-decode'; export default (type, params) => { if (type === AUTH_LOGIN) { const { username, password } = params; let data = JSON.stringify({ username, password }); return axios.post('http://localhost:5000/api/login/', data, { headers: { 'Content-Type': 'application/json', } }).then(res => { if (res.data.error || res.status !== 200) { throw new Error(res.data.error); } else { const token = res.data.token; const decodedToken = decodeJwt(token); const role = decodedToken.identity.role; localStorage.setItem('token', token); localStorage.setItem('role', role); return Promise.resolve(); } }); } if (type === AUTH_LOGOUT) { localStorage.removeItem('token'); localStorage.removeItem('role'); return Promise.resolve(); } if (type === AUTH_ERROR) { const { status } = params; if (status === 401 || status === 403) { localStorage.removeItem('token'); localStorage.removeItem('role'); return Promise.reject(); } return Promise.resolve(); } if (type === AUTH_CHECK) { return localStorage.getItem('token') ? Promise.resolve() : Promise.reject({ redirectTo: '/login' }); } if (type === AUTH_GET_PERMISSIONS) { const role = localStorage.getItem('role'); return role ? Promise.resolve(role) : Promise.reject(); } }; 


现在,为了好玩,我们转到后端,转到路由: /api/protect-area/ ,并将结果粘贴到主页面上,那里有大胡子。

为此,请使用以下代码替换文件react-admin/examples/demo/src/dashboard/Welcome.js的内容:

管理员/示例/演示/ src /仪表板/ Welcome.js
 import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import CardMedia from '@material-ui/core/CardMedia'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; import HomeIcon from '@material-ui/icons/Home'; import CodeIcon from '@material-ui/icons/Code'; import { makeStyles } from '@material-ui/core/styles'; import { useTranslate } from 'react-admin'; const useStyles = makeStyles({ media: { height: '18em', }, }); const mediaUrl = `https://marmelab.com/posters/beard-${parseInt( Math.random() * 10, 10 ) + 1}.jpeg`; const Welcome = () => { const [state, setState] = useState({}); const fetchFlask = useCallback(async () => { axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token'); await axios.get('http://localhost:5000/api/protect-area/').then(res => { const answer = res.data.answer; setState({ answer }); }); }, []); useEffect(() => { fetchFlask(); }, []); // eslint-disable-line react-hooks/exhaustive-deps const translate = useTranslate(); const classes = useStyles(); return ( <Card> <CardMedia image={mediaUrl} className={classes.media} /> <CardContent> <Typography variant="h5" component="h2"> {state.answer} </Typography> <Typography component="p"> {translate('pos.dashboard.welcome.subtitle')} </Typography> </CardContent> <CardActions style={{ justifyContent: 'flex-end' }}> <Button href="https://marmelab.com/react-admin"> <HomeIcon style={{ paddingRight: '0.5em' }} /> {translate('pos.dashboard.welcome.aor_button')} </Button> <Button href="https://github.com/marmelab/react-admin/tree/master/examples/demo"> <CodeIcon style={{ paddingRight: '0.5em' }} /> {translate('pos.dashboard.welcome.demo_button')} </Button> </CardActions> </Card> ); }; export default Welcome; 


转到地址:

localhost:3000
通过输入登录名/密码登录:admin / habr

如果有所有规则,那么我们将在首页的标题中看到42。

像这样:



选配


  • 除了Flask-RESTful,还有Flask-RESTplus, 在这里您可以看一下讨论,这是好是坏
  • 您可以在前面编写一个管理面板,然后运行: npm run build您将获得现成的静态文件,flask可以将其作为模板呈现。 更多细节在这里 。 这样,您就无需让Web服务器负责React运行。

Source: https://habr.com/ru/post/zh-CN477126/


All Articles