
如果您需要一个快速的管理
面板 ,前端是
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(); }, []);
转到地址:
localhost:3000
通过输入登录名/密码登录:admin / habr
如果有所有规则,那么我们将在首页的标题中看到42。
像这样:

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