Tutorial

Comment traiter les données des requêtes entrantes dans Flask

PythonPython Frameworks

Introduction

Les applications web doivent souvent traiter les données des requêtes entrantes fournies par les utilisateurs. Cette charge utile peut prendre la forme de chaînes de requête, de données de formulaires et d'objets JSON. Flask, comme tout autre framework web, vous permet d'accéder aux données d'une requête.

Au cours de ce tutoriel, vous allez créer une application Flask avec trois itinéraires qui acceptent soit les chaînes de requête, les données de formulaire ou les objets JSON.

Conditions préalables

Pour suivre ce tutoriel, vous aurez besoin de :

Ce tutoriel a été vérifié avec Pipenv v2020.11.15, Python v3.9.0 et Flask v1.1.2.

Créer le projet

Afin de pouvoir découvrir les différentes façons d'utiliser des requêtes, vous devrez créer une application Flask. Même si l'application donnée en exemple utilise une structure simplifiée pour les fonctions et les itinéraires de visualisation, vous pouvez appliquer tout ce que vous apprendrez au cours de ce tutoriel à toute méthode qui permet d'organiser vos affichages sous forme, par exemple, d'un affichage par catégories, de plans d'action ou d'une extension comme Flask-Via.

Vous devrez tout d'abord créer un répertoire de projet. Ouvrez votre terminal et exécutez la commande suivante :

  • mkdir flask_request_example

Ensuite, naviguez vers le nouveau répertoire :

  • cd flask_request_example

Ensuite, installez Flask. Ouvrez votre terminal et exécutez la commande suivante :

  • pipenv install Flask

La commande pipenv créera un virtualenv pour ce projet, un Pipfile, installera flask et un Pipfile.lock.

Pour activer le virtualenv du projet, exécutez la commande suivante :

  • pipenv shell

Pour accéder aux données entrantes dans Flask, vous devez utiliser l'objet request. L'objet request contient toutes les données entrantes de la requête, qui incluent, entre autres, le typemime, le référent, l'adresse IP, les données brutes, la méthode HTTP et les en-têtes.

Bien que toutes les informations que contient l'objet request soient utiles, pour les besoins de cet article, vous allez vous concentrer sur les données qui sont normalement fournies directement par l'appelant du terminal.

Pour accéder à l'objet de requête dans Flask, vous devez l'importer à partir de la bibliothèque Flask :

from flask import request

Vous aurez alors la possibilité de l'utiliser dans l'une de vos fonctions d'affichage.

Utilisez votre éditeur de code pour créer un fichier app.py. Importez Flask et l'objet request. Configurez également des itinéraires pour query-example, form-example et json-example:

app.py
# import main Flask class and request object
from flask import Flask, request

# create the Flask app
app = Flask(__name__)

@app.route('/query-example')
def query_example():
    return 'Query String Example'

@app.route('/form-example')
def form_example():
    return 'Form Data Example'

@app.route('/json-example')
def json_example():
    return 'JSON Object Example'

if __name__ == '__main__':
    # run app in debug mode on port 5000
    app.run(debug=True, port=5000)

Ensuite, ouvrez votre terminal et démarrez l'application avec la commande suivante :

  • python app.py

L'application démarrera sur le port 5000. Vous pourrez alors visualiser chaque itinéraire dans votre navigateur en utilisant les liens suivants :

http://127.0.0.1:5000/query-example (or localhost:5000/query-example)
http://127.0.0.1:5000/form-example (or localhost:5000/form-example)
http://127.0.0.1:5000/json-example (or localhost:5000/json-example)

Le code établit trois itinéraires. En visualisant chaque itinéraire, les messages « Requy String Example », « Form Data example » et « JSON Object example » s'afficheront respectivement.

Utiliser les arguments de requête

On utilise souvent les arguments URL que vous ajoutez à une chaîne de requête pour transmettre des données à une application web. Alors qu'en navigant sur le Web, vous rencontrerez probablement une chaîne de requête auparavant.

Une chaîne de requête ressemble à ce qui suit :

example.com?arg1=value1&arg2=value2

La chaîne de requête commence après le caractère du point d'interrogation (?) :

example.com?arg1=value1&arg2=value2

Et intègre des paires de valeurs clés séparées par un caractère de perluète (&) :

example.com?arg1=value1&arg2=value2

Pour chaque pair, la clé est suivie du caractère égal (=), puis de la valeur.

arg1 : value1
arg2 : value2

Les chaînes de requête vous seront utiles pour transmettre les données sur lesquelles l'utilisateur n'a pas besoin d'agir. Vous pouvez générer une chaîne de requête quelque part dans votre application et l'ajouter à une URL afin que, lorsqu'un utilisateur soumet une requête, les données soient automatiquement transmises pour elles. Une chaîne de requête peut également être générée par les formulaires qui ont pour méthode GET.

Ajoutons une chaîne de requête à l'itinéraire query-example. Dans cet exemple hypothétique, vous allez renseigner le nom d'un langage de programmation qui s'affichera à l'écran. Créez une clé de « langage » et une valeur de « Python » :

http://127.0.0.1:5000/query-example?language=Python

Si vous exécutez l'application et que vous naviguez vers cette URL, vous verrez qu'elle affiche toujours le message « Request String Example ».

Vous devrez alors programmer la partie qui gère les arguments de la requête. Ce code lira le contenu de la clé language en utilisant soit request.args.get('language') ou request.args['language'].

En appelant request.args.get('language'), l'exécution de l'application continuera si la clé language n'existe pas dans l'URL. Le cas échéant, la méthode affichira le résultat None.

En appelant request.args['language'], l'application renverra une erreur 400 si la clé de la langue n'existe pas dans l'URL.

Lorsque vous travaillez avec des chaînes de requête, nous vous recommandons d'utiliser request.args.get() pour empêcher l'application d'entrer en échec.

Lisons la clé language et affichons-la comme la sortie.

Modifiez l'itinéraire query-example dans app.py en utilisant le code suivant :

app.py
@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    return '''<h1>The language value is: {}</h1>'''.format(language)

Ensuite, exécutez l'application et naviguez jusqu'à l'URL :

http://127.0.0.1:5000/query-example?language=Python

Le navigateur devrait afficher le message suivant :

Output
The language value is: Python

L'argument provenant de l'URL est affecté à la variable language et ensuite renvoyé au navigateur.

Pour ajouter d'autres paramètres de chaîne de requête, vous pouvez ajouter les perluètes et de nouvelles paires clé-valeur à la fin de l'URL. Créez une clé « framework » et une valeur « Flask » :

http://127.0.0.1:5000/query-example?language=Python&framework=Flask

Et si vous en voulez plus, continuez à ajouter des perluettes et des paires clé-valeur. Créez une clé « website » et une valeur « DigitalOcean » :

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

Pour accéder à ces valeurs, vous devrez à nouveau utiliser soit request.args.get() ou request.args[]. Utilisons-les toutes les deux pour voir ce qu'il se passe lorsqu'une clé est manquante. Modifiez l'itinéraire query_example pour attribuer la valeur des résultats à des variables, puis affichez-les :

@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    # if key doesn't exist, returns a 400, bad request error
    framework = request.args['framework']

    # if key doesn't exist, returns None
    website = request.args.get('website')

    return '''
              <h1>The language value is: {}</h1>
              <h1>The framework value is: {}</h1>
              <h1>The website value is: {}'''.format(language, framework, website)

Ensuite, exécutez l'application et naviguez jusqu'à l'URL :

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

Le navigateur devrait afficher le message suivant :

Output
The language value is: Python The framework value is: Flask The website value is: DigitalOcean

Supprimez la clé language de l'URL :

http://127.0.0.1:5000/query-example?framework=Flask&website=DigitalOcean

Le navigateur devrait afficher le message suivant accompagné de None si aucune valeur language n'est fournie :

Output
The language value is: None The framework value is: Flask The website value is: DigitalOcean

Supprimez la clé framework de l'URL :

http://127.0.0.1:5000/query-example?language=Python&website=DigitalOcean

Le navigateur devrait rencontrer une erreur, étant donné qu'il s'attend à trouver une valeur pour framework :

Output
werkzeug.exceptions.BadRequestKeyError werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand. KeyError: 'framework'

Vous comprenez maintenant de quelle manière sont traitées les chaînes de requête. Passons au prochain type de données entrantes.

Utiliser les données de formulaire

Les données des formulaires proviennent d'un formulaire qui a été envoyé en tant que requête POST à un itinéraire. Par conséquent, au lieu de voir les données dans l'URL (sauf si le formulaire est soumis avec une requête GET), les données du formulaire seront transmises à l'application en coulisses. Même si vous ne pouvez pas facilement voir les données du formulaire qui sont transmises, votre application est tout de même en capacité de les lire.

Pour le démontrer, modifiez l'itinéraire form-example dans app.py pour accepter les requêtes à la fois GET et POST. Le formulaire suivant est renvoyé :

app.py
# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    return '''
              <form method="POST">
                  <div><label>Language: <input type="text" name="language"></label></div>
                  <div><label>Framework: <input type="text" name="framework"></label></div>
                  <input type="submit" value="Submit">
              </form>'''

Ensuite, exécutez l'application et naviguez jusqu'à l'URL :

http://127.0.0.1:5000/form-example

Le navigateur devrait afficher un formulaire avec deux champs de saisie (un pour language et un pour framework) et un bouton Submit.

Le plus important à savoir sur ce formulaire, c'est qu'il effectue une requête POST sur le même itinéraire qui a généré le formulaire. Les clés qui seront lues dans l'application proviennent toutes des attributs name qui se trouvent sur nos entrées de formulaire. Dans ce cas, langage et framework correspondent aux noms des entrées. Vous y aurez donc accès dans l'application.

Dans la fonction d'affichage, vous devrez alors vérifier si la méthode de requête est GET ou POST. S'il s'agit d'une requête GET, vous pouvez afficher le formulaire. Dans le cas contraire, s’il s'agit d'une requête POST, vous devrez alors traiter les données entrantes.

Modifiez l'itinéraire form-example dans app.py en utilisant le code suivant :

app.py
# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    # handle the POST request
    if request.method == 'POST':
        language = request.form.get('language')
        framework = request.form.get('framework')
        return '''
                  <h1>The language value is: {}</h1>
                  <h1>The framework value is: {}</h1>'''.format(language, framework)

    # otherwise handle the GET request
    return '''
           <form method="POST">
               <div><label>Language: <input type="text" name="language"></label></div>
               <div><label>Framework: <input type="text" name="framework"></label></div>
               <input type="submit" value="Submit">
           </form>'''

Ensuite, exécutez l'application et naviguez jusqu'à l'URL :

http://127.0.0.1:5000/form-example

Renseignez le champ language avec la valeur Python et le champ framework avec la valeur Flask. Ensuite, appuyez sur Submit.

Le navigateur devrait afficher le message suivant :

Output
The language value is: Python The framework value is: Flask

Vous comprenez maintenant de quelle manière sont traitées les chaînes de requête. Passons au prochain type de données entrantes.

Utiliser des données JSON

Les données JSON sont généralement construites par un processus qui appelle l'itinéraire.

Voici à quoi ressemble un exemple d'objet JSON :

{
  "language": "Python",
  "framework": "Flask",
  "website": "Scotch",
  "version_info": {
    "python": "3.9.0",
    "flask": "1.1.2"
  },
  "examples": ["query", "form", "json"],
  "boolean_test": true
}

Cette structure peut permettre la transmission de données bien plus complexes par opposition aux chaînes de requête et aux données de formulaires. Dans l'exemple, vous voyez des objets JSON imbriqués et un tableau d'éléments. Flask peut gérer ce format de données.

Modifiez l'itinéraire form-example dans app.py pour accepter les requêtes POST et ignorer les autres requêtes comme GET :

app.py
@app.route('/json-example', methods=['POST'])
def json_example():
    return 'JSON Object Example'

Contrairement au navigateur web qui sert à interroger les chaînes et les données de formulaire, pour les besoins de cet article, afin d'envoyer un objet JSON, vous utiliserez Postman pour envoyer des requêtes personnalisées aux URL.

Remarque : si vous avez besoin d'aide pour naviguer sur l'interface Postman pour les requêtes, consultez la documentation officielle.

Dans Postman, ajoutez l'URL et configurez le type sur POST. Sur l'onglet body, changez la valeur sur raw et sélectionnez JSON dans la liste déroulante.

Ces paramètres sont nécessaires pour permettre à Postman d'envoyer les données JSON correctement. Par conséquent, votre application Flask comprendra qu'elle reçoit JSON :

POST http://127.0.0.1:5000/json-example
Body
raw JSON

Ensuite, copiez l'exemple JSON précédent dans la saisie de texte.

Envoyez la requête. Vous devriez recevoir la réponse « JSON Object Example ». Il s'agit d'une approche assez étrange, mais il faudra vous y attendre, car il reste encore à écrire le code qui permet de gérer la réponse des données JSON.

Pour lire les données, vous devez comprendre de quelle manière Flask traduit les données JSON en structures de données Python :

  • Tout ce qui est un objet est converti en un dict. Python {"key" : "value"}, dans JSON cela correspond à somedict['key'] qui renvoie une valeur dans Python.
  • Un tableau dans JSON est converti en une liste dans Python. Étant donné que la syntaxe est la même, voici un exemple de liste : [1,2,3,4,5]
  • Les valeurs entre crochets dans l'objet JSON deviennent des chaînes dans Python.
  • Les valeurs booléennes true et false deviennent True et False dans Python.
  • Enfin, les numéros sans parenthèse se transforment en numéros dans Python.

Travaillons maintenant sur le code afin de pouvoir lire les données JSON entrantes.

Tout d'abord, attribuons tout ce qui se trouve dans l'objet JSON à une variable en utilisant request.get_json().

request.get_json() convertit l'objet JSON en données Python. Attribuons maintenant les données des requêtes entrantes aux variables et renvoyons-les en apportant les modifications suivantes à l'itinéraire json-example :

app.py
# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = request_data['language']
    framework = request_data['framework']

    # two keys are needed because of the nested object
    python_version = request_data['version_info']['python']

    # an index is needed because of the array
    example = request_data['examples'][0]

    boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

Notez de quelle manière vous accédez aux éléments qui ne sont pas au niveau supérieur. ['version']['python'] est utilisé, car vous êtes en train de saisir un objet imbriqué. Et ['example'][0] permet d'accéder à l'index 0 dans le tableau des exemples.

Si l'objet JSON envoyé avec la requête n'est pas accessible dans votre fonction de visualisation, cela signifie que la requête échouera. Si vous ne souhaitez pas qu'elle échoue en l'absence d'une clé, il vous faudra préalablement vérifier si la clé existe avant d'essayer d'y accéder.

app.py
# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = None
    framework = None
    python_version = None
    example = None
    boolean_test = None

    if request_data:
        if 'language' in request_data:
            language = request_data['language']

        if 'framework' in request_data:
            framework = request_data['framework']

        if 'version_info' in request_data:
            if 'python' in request_data['version_info']:
                python_version = request_data['version_info']['python']

        if 'examples' in request_data:
            if (type(request_data['examples']) == list) and (len(request_data['examples']) > 0):
                example = request_data['examples'][0]

        if 'boolean_test' in request_data:
            boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

Exécutez l'application et envoyez l'exemple de requête JSON en utilisant Postman. Dans la réponse, vous obtiendrez la sortie suivante :

Output
The language value is: Python The framework value is: Flask The Python version is: 3.9 The item at index 0 in the example list is: query The boolean value is: false

Désormais, vous comprenez de quelle manière sont manipulés les objets JSON.

Conclusion

Au cours de ce tutoriel, vous avez créé une application Flask avec trois itinéraires qui acceptent soit les chaînes de requête, les données de formulaire ou les objets JSON.

En outre, n'oubliez pas que toutes les approches ont bien pris en compte la problématique récurrente de l'échec normal généré par l'absence d'une clé.

Avertissement : le nettoyage des entrées des utilisateurs n'a pas été abordé dans cet article. Le nettoyage des entrées des utilisateurs permet de garantir que les données lues par l'application ne génèrent pas la défaillance inattendue d'un élément ou ne contournent pas les mesures de sécurité.

Si vous souhaitez en savoir plus sur Flask, veuillez consulter notre page thématique Flask dans laquelle vous trouverez des exercices et des projets de programmation.

Creative Commons License