Admin en 5 minutes. Frontend - react-admin, backend - Flask-RESTful



Si vous avez besoin d'obtenir rapidement le panneau d' administration sur le genou, où le front-end est react-admin et le back - end est Flask-RESTful api , alors le code minimum est de plusieurs dizaines de lignes ci-dessous pour l'implémenter.

API Flask-RESTful


Le code lui-même se compose d'un fichier 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') 

Passons en revue le code:

  • Notre backend effectuera toutes les interactions avec le monde extérieur uniquement via l'API RESTful, même les autorisations dans le panneau d'administration le sont également. Pour cela, flask a un module pratique: Flask-RESTful api
  • Le module flask_jwt_extended nous servira à protéger les routes qui ne sont accessibles qu'après autorisation. Il n'y a rien de sacré ici, juste un jeton jwt ( JSON Web Token ) sera ajouté à chaque requête http, par lequel notre application comprendra que l'utilisateur est autorisé.
    Le code ci-dessus montre que le décorateur @jwt_required est utilisé à cet effet. Vous pouvez l'ajouter aux routes d'API qui doivent être protégées.
  • Sans flask_cors, nous obtenons l'erreur suivante:
    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.
    En savoir plus sur CORS ici .

Nous mettons toutes les bibliothèques nécessaires et exécutons le code avec la commande:

 python main.py 

Comme vous pouvez le voir, j'ai codé en dur le nom d'utilisateur et le mot de passe du panneau d'administration: admin / habr.

Une fois le flacon démarré, vous pouvez tester sa fonctionnalité à l'aide de curl:

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

Si un tel résultat:

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

Donc tout est correct et vous pouvez vous déplacer vers l'avant.

Frontend react-admin


J'ai aimé react-admin. Voici la documentation , et voici la version de démonstration:
https://marmelab.com/react-admin-demo/#/login
Connexion: démo
Mot de passe: démo

Afin d'obtenir le même panneau d'administration que dans la démo, nous exécutons les commandes suivantes:

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

Maintenant, nous devons lui apprendre à interagir avec notre backend.

Pour ce faire, remplacez le contenu du admin/examples/demo/src/authProvider.js par le code suivant, qui sera responsable de l'autorisation, de la sortie du panneau d'administration, etc.:

admin / examples / demo / 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(); } }; 


Et maintenant, pour le plaisir, nous nous tournons vers notre backend, vers la route: /api/protect-area/ et collons le résultat à la page d'administration principale, où il y a des hommes barbus.

Pour ce faire, remplacez le contenu du fichier react-admin/examples/demo/src/dashboard/Welcome.js par le code suivant:

admin / examples / demo / src / dashboard / 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; 


Allez à l'adresse:

localhost:3000
Connectez-vous en entrant login / pass: admin / habr

Et si toutes les règles, alors nous verrons 42 dans l'en-tête sur la page principale.

Comme ça:



En option


  • En plus de Flask-RESTful, il y a aussi Flask-RESTplus, ici vous pouvez regarder la discussion, qui est meilleure ou pire
  • Vous pouvez écrire un panneau d'administration sur le devant, puis exécuter: npm run build - vous obtenez des fichiers statiques prêts à l'emploi que flask peut rendre comme un modèle. Plus de détails ici . Et de cette façon, vous pouvez éliminer la nécessité de garder le serveur Web responsable de la réactivité.

Source: https://habr.com/ru/post/fr477126/


All Articles