[PYTHON] Vérifiez simplement Content-Type avec Flask (@content_type)

1.Tout d'abord

Dans Article précédent, j'ai créé une application conceptuelle pour les micro-services utilisant flask, cerberus et peewee. Cette fois, je vais vous expliquer comment vérifier Content-Type par flacon, qui a été omis à ce moment-là. En outre, je voudrais vous présenter comment créer un décorateur de fonction qui vérifie Content-Type.

1.1. Spécifications du décorateur de fonction @ content_type introduites cette fois

@content_exemple d'utilisation du décorateur de fonction de type


# rest api
@app.route('/questions', methods=['POST'])
@content_type('application/json')
def register_question():

1.2. Environnement de vérification

2. Voir request.headers pour les en-têtes de requête HTTP

Dans flask, vous pouvez vous référer aux informations d'en-tête de requête HTTP avec request.headers. request.headers est un objet de type dictionnaire dont la clé est le nom de l'en-tête. Ainsi, lors de la vérification de Content-Type, obtenez la valeur en utilisant Content-Type comme clé. Voir http://flask.pocoo.org/docs/0.12/api/#incoming-request-data pour plus d'informations.

2.1. Exemple de code pour vérifier Content-Type (pas assez bon)

Pas un bon exemple de code


# -*- coding: utf-8 -*-
import os
from flask import Flask, abort, request, make_response, jsonify

# flask
app = Flask(__name__)

# rest api
@app.route('/questions', methods=['POST'])
def register_question():
    #Type de contenu (application/json) vérifier
    if not request.headers.get("Content-Type") == 'application/json':
        error_message = {
            'error':'not supported Content-Type'
        }
        return make_response(jsonify(error_message), 400)

    # omitted :Exécution de la logique métier
    #Retour du résultat du traitement (format JSON)
    return make_response(jsonify({'result': True}))

@app.route('/questions/<string:question_code>', methods=['PUT'])
def update_question(question_code):
    #Type de contenu (application/json) vérifier
    if not request.headers.get("Content-Type") == 'application/json':
        error_message = {
            'error':'not supported Content-Type'
        }
        return make_response(jsonify(error_message), 400)
    
    # omitted :Exécution de la logique métier
    #Retour du résultat du traitement (format JSON)
    return make_response(jsonify({'result': True}))

# main
if __name__ == "__main__":
    app.run(host=os.getenv("APP_ADDRESS", 'localhost'), \
    port=os.getenv("APP_PORT", 3000))

2.2. Problèmes avec l'exemple de code

Comme il existe de nombreux endroits pour vérifier le type de contenu, des problèmes de clonage / duplication de code se produiront. Elle peut être définie comme une fonction d'utilité, mais comme elle ne remplit pas les conditions préalables avant d'appeler l'API, c'est juste une impression personnelle, mais je ne voulais pas décrire le processus d'appel de l'utilitaire.

Un décorateur de fonction vous permet d'appeler la fonction cible (API REST dans ce cas) uniquement si la vérification est réussie, c'est-à-dire si les conditions préalables sont remplies. En conséquence, j'ai décidé d'implémenter le décorateur de fonction illustré ci-dessous.

3. Créez un décorateur de fonction @ content_type pour un look moderne

3.1. Exemple de code pour vérifier Content-Type

content_type_check_app.py


# -*- coding: utf-8 -*-
import os
from flask import Flask, abort, request, make_response, jsonify
import functools

# flask
app = Flask(__name__)

# check content-type decorator
def content_type(value):
    def _content_type(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            if not request.headers.get("Content-Type") == value:
                error_message = {
                    'error': 'not supported Content-Type'
                }
                return make_response(jsonify(error_message), 400)

            return func(*args,**kwargs)
        return wrapper
    return _content_type

# rest api
@app.route('/questions', methods=['POST'])
@content_type('application/json')
def register_question():
    # omitted :Exécution de la logique métier
    #Retour du résultat du traitement (format JSON)
    return make_response(jsonify({'result': True}))

@app.route('/questions/<string:question_code>', methods=['PUT'])
@content_type('application/json')
def update_question(question_code):
    # omitted :Exécution de la logique métier
    #Retour du résultat du traitement (format JSON)
    return make_response(jsonify({'result': True}))

# main
if __name__ == "__main__":
    app.run(host=os.getenv("APP_ADDRESS", 'localhost'), \
    port=os.getenv("APP_PORT", 3000))

3.2. Résultat de l’utilisation de «@ content_type»

Vous pouvez maintenant simplement ajouter @ content_type ('application / json') aux fonctions de l'API REST. Vous pouvez voir intuitivement que Content-Type doit être application / json sans le problème de clonage de code. (Si nécessaire, rendez application / json constante)

** Au fait, si vous modifiez l'ordre de description de @ app.route et @ content_type, une erreur se produira. ** ** Les décorateurs de fonction Python ne sont pas des points de repère comme les annotations Java, mais une imbrication de fonction, donc l'ordre d'imbrication affecte le flux de traitement. C'est parce que la fonction de flask est appelée dans le traitement de @ content_type, il est donc nécessaire d'exécuter d'abord flask en tant que nest.

4. Contrôle de fonctionnement

4.1. Code source du client

democlient.py


# -*- coding: utf-8 -*-
import requests
import json

def register_question_none_content_type(url, data):
    print("[POST] /questions : none content-type")
    response = requests.post(url, json.dumps(question))
    print(response.status_code)
    print(response.content)

def register_question(url, data):
    print("[POST] /questions : content-type application/json")
    response = requests.post(url, json.dumps(question), \
        headers={'Content-type': 'application/json'})
    print(response.status_code)
    print(response.content)

# main
if __name__ == "__main__":
    url = 'http://localhost:3000/questions'
    question = {
        'question_code' : '9999999999',
        'category' : 'demo',
        'message' : 'hello'
    }
    # Content-Aucun type
    register_question_none_content_type(url, question)
    # Content-Type : application/json
    register_question(url, question)

4.1.1. Ne spécifiez pas l'application / json Content-Type dans les requêtes

Comme décrit dans Article précédent, si vous spécifiez des données dans l'argument json comme json = data, les requêtes s'appliqueront automatiquement / Définissez le Content-Type de json. Puisque cette fonction n'est pas utilisée cette fois, les données de transmission directe (json.dumps (question)) sont définies dans le deuxième argument.

4.2. Démarrez le serveur

Démarrage du serveur (et journal d'exécution)


C:\tmp\>python content_type_check_app.py
 * Running on http://localhost:3000/ (Press CTRL+C to quit)
127.0.0.1 - - [16/Aug/2017 20:02:30] "POST /questions HTTP/1.1" 400 -
127.0.0.1 - - [16/Aug/2017 20:02:30] "POST /questions HTTP/1.1" 200 -

La première ligne du journal d'exécution est une requête HTTP pour laquelle Content-Type n'est pas spécifié et l'état HTTP est 400. La deuxième ligne correspond à la requête HTTP spécifiée. Dans ce cas, le processus est exécuté normalement et 200 est renvoyé.

4.3. Démarrage du client

Exécution du client


C:\tmp\python democlient.py
[POST] /questions : none content-type
400
{
  "error": "not supported Content-Type"
}

[POST] /questions : content-type application/json
200
{
  "result": true
}

Nous avons confirmé que les requêtes HTTP qui ne spécifient pas Content-Type renverront une erreur 400 avec le statut HTTP et le contenu de l'erreur {'error': 'not supported Content-Type'} comme prévu. Étant donné que la demande suivante spécifie application / json pour Content-Type, elle réussit la vérification Content-Type et renvoie le résultat de traitement correct avec un statut HTTP de 200.

5. Enfin

Cette fois, j'ai expliqué comment vérifier Content-Type avec flask. ** Si le flask peut effectuer le mappage de requêtes même avec Content-Type, Content-Type sera garanti lorsque l'API REST sera appelée **, donc le @ content_type créé cette fois ne sera pas nécessaire.

De plus, j'ai créé un décorateur de fonctions pour la première fois cette fois, mais comme je peux ajouter des fonctions plus tard comme AOP, j'ai senti qu'il pouvait être utilisé de diverses autres manières.

Recommended Posts

Vérifiez simplement Content-Type avec Flask (@content_type)
Téléchargeur d'images avec Flask
Variables d'environnement HTTP dans Flask
Traitement asynchrone du céleri dans Flask
Télécharger plusieurs fichiers avec Flask
Rechercher des commandes externes avec python
Créer un lecteur CSV avec Flask
Correction d'Apscheduler fonctionnant deux fois dans Flask
À propos du traitement des paramètres dans le fichier request.args de Flask
Vérifiez et déplacez le répertoire en Python
Vérifiez le résumé des données dans CASTable
Meilleures pratiques pour les fichiers de configuration dans Flask