Développement basé sur les schémas avec Responder: essayez d'afficher l'interface utilisateur Swagger

Qu'est-ce que le développement piloté par des schémas?

En termes simples, il s'agit d'une méthode de développement qui détermine d'abord le schéma de l'API (définition des types de requête et de réponse et des noms de champ), génère systématiquement des documents et se moque avec lui, puis l'implémente en interne. (Référence: Exemple d'explication et d'implémentation du développement piloté par schéma pour l'optimisation du développement d'API Web d'équipe)

Je voudrais faire du développement piloté par schéma avec responder, qui est un framework web de python.

Le but est de pouvoir voir le Swagger suivant. ezgif-1-4c611adeacb7.gif

responder Un framework Web moderne pour Python. En termes de volume, c'est un microframework comme un flacon ou une bouteille. Écrivez simplement ce qui suit et exécutez-le comme $ python xxx.py``` pour démarrer le serveur d'API qui renvoie JSON.

import responder

api = responder.API()

@api.route("/")
async def view(req, resp):
    resp.media = {'success': True}

if __name__ == "__main__":
    api.run()

Il convient également de noter qu'il possède des fonctionnalités modernes telles que le serveur Web asynchrone (ASGI), Websocket, GraphQL et la génération automatique de schéma OpenAPI. Environ un an après sa sortie, c'est un monstre avec plus de 3300 étoiles sur github (en novembre 2019), et la fréquence des commits est élevée, donc je pense qu'il a un bel avenir.

référence:

Portée de cet article

Les méthodes de développement suivantes dans responder sont résumées.

  1. Définition de schéma et moquage: (sérialisation par mashmallow)
  2. Maquette et documentation fournies: (Conversion vers OpenAPI 3.0 (swagger) par apispec)

Correspond au chapitre sur la prise en charge du schéma d'API ouvert et la documentation interactive dans la visite des fonctionnalités du répondeur (https://responder.readthedocs.io/en/latest/tour.html#openapi-schema-support), mais modeste Puisqu'il n'y a qu'une introduction, j'aimerais avoir une explication pratique.

Constitution

Par souci d'explication, nous allons créer l'API simple suivante.

method une fonction
get Renvoie tous les identifiants existants
post Renvoie si l'ID entré existe

De plus, dans la publication, si une erreur de validation se produit, le contenu de l'erreur sera renvoyé.

version responder: v2.0.3 python: v3.6.5

Définition de schéma et création de maquette par guimauve

mashmallow est une bibliothèque qui prend en charge la sérialisation des objets. Si vous définissez une classe de schéma et chargez / videz une demande ou une réponse via la classe de schéma, la validation sera appliquée. Le flux est le suivant.

from marshmallow import Schema, fields

class AllIdRespSchema(Schema):
    #Définition de schéma(liste int)
    exist_ids = fields.List(fields.Int())

class AllExistIDModel():
    #réponse simulée
    #Remplacez ce contenu pour une production ultérieure
    def __init__(self):
        self.exist_ids = [1, 2, 3, 4, 5]

@api.route("/schema_driven")
async def schema_driven_view(req, resp):
    if req.method == "get":
        # validate response data
        resp.media = AllIdRespSchema().dump(AllExistIDModel())

Après ce flux, la classe Schema et la classe fictive requises dans cet exemple sont définies comme suit. AllIdRespSchema, AllExistIDModel sont définis dans l'exemple ci-dessus.

Nom du schéma mock Utilisation
AllIdRespSchema AllExistIDModel avoir une réponse
IdReqSchema Aucun post demande
IsExistRespSchema IsExistIDModel post réponse
ErrorRespSchema ErrorModel Réponse en cas d'erreur de validation
class IdReqSchema(Schema):
    # if required=True, the field must exist
    id = fields.Int(required=True)

class IsExistRespSchema(Schema):
    is_exist = fields.Bool()

class ErrorRespSchema(Schema):
    error = fields.Str()
    errorDate = fields.Date()
class IsExistIDModel():
    def __init__(self, data):
        id = data.get('id')
        self.is_exist = id in [1, 2, 3, 4, 5]

class ErrorModel():
    def __init__(self, error):
        self.error = str(error)
        self.errorDate = datetime.datetime.now()

Si vous définissez le schéma de cette manière, il vous suffit d'écrire le flux de processus pour la vue.

from marshmallow import ValidationError

@api.route("/schema_driven")
async def schema_driven_view(req, resp):
    if req.method == "get":
        # validate response data
        resp.media = AllIdRespSchema().dump(AllExistIDModel())

    elif req.method == "post":
        request = await req.media()
        try:
            # validate request data using Schema
            data = IdReqSchema().load(request)

        except ValidationError as error:
            # raise ValidationError
            resp.status_code = api.status_codes.HTTP_400
            # validate response data
            resp.media = ErrorRespSchema().dump(ErrorModel(error))
            return

        # validate response data
        resp.media = IsExistRespSchema().dump(IsExistIDModel(data))

Comme vous pouvez le voir, le contenu de la vue est écrit uniquement avec le flux de traitement abstrait et le nom de classe pour la classe Schema et la maquette. Donc, je pense que le contenu de la vue ne sera pas changé avec un petit changement (changement de type ou de nom de champ de json).

Ensuite, je voudrais générer un document à partir de ce schéma afin que je puisse obtenir des commentaires.

Maquette et document fournis par apispec

De là, nous utiliserons la fonction répondeur. Cependant, il s'agit en fait d'un wrapper pour une bibliothèque appelée apispec. Tout ce que nous faisons, c'est simplement laisser les définitions de docstring et de schéma couler dans l'apispec presque telles quelles. Donc, si vous souhaitez le personnaliser en détail, vous devez vous référer à la définition d'apispec et d'OpenAPI 3.0.

apispec analyse la définition de schéma et les docstrings de marshmallow, les transforme au format Open API et génère une interface utilisateur Swagger.

référence:

Tout d'abord, vous devez ajouter un argument comme indiqué ci-dessous lors de la déclaration d'une api.

api = responder.API(
    openapi='3.0.0',  # OpenAPI version
    docs_route='/docs',  # endpoint for interactive documentation by swagger UI. if None, this is not available.
)

Schema Ensuite, utilisez le décorateur api.schema pour définir la définition du schéma. Cependant, écrire la définition de schéma dans la vue principale semble trop compliqué, je voudrais donc importer la définition de schéma à partir d'un autre fichier. Donc, je vais le déclarer de force comme suit.

api.schema("IdReqSchema")(IdReqSchema)
api.schema("ErrorRespSchema")(ErrorRespSchema)
api.schema("IsExistRespSchema")(IsExistRespSchema)
api.schema("AllIdRespSchema")(AllIdRespSchema)

Si vous démarrez le serveur et accédez aux éléments suivants, vous pouvez vérifier la définition du schéma dans l'interface utilisateur de Swagger comme indiqué ci-dessous. 127.0.0.1:5042/docs

ezgif-1-31f99e6a7002.gif

API GET Ajoutez la docstring au format yaml à la définition de vue ci-dessus, comme indiqué ci-dessous. Vous pouvez décrire l'API dans la description. Vous pouvez également spécifier le schéma de réponse pour chaque code d'état. Si vous définissez le schéma comme ci-dessous, vous pouvez vous référer à la définition de schéma de la réponse et à l'exemple de réponse de Swagger UI. Étant donné que vous avez atteint l'API lors du référencement de l'exemple de réponse, vous pouvez également vérifier si une erreur de serveur interne se produit.

@api.route("/schema_driven")
async def schema_driven_view(req, resp):
    """exist id checker endpoint.
    ---
    name: is_exist
    get:
        description: Get the all exist id
        responses:
            200:
                description: All exist_id to be returned
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/AllIdRespSchema"
    """
    ...

De même, si vous cochez Swagger,

ezgif-1-4c611adeacb7.gif

POST De plus, ajoutez une docstring pour la publication. Cela ne figure pas dans la documentation officielle. Pour plus de détails, vous devez vous référer à Open API Definition.

Vous pouvez écrire une réponse de la même manière que get. Écrivons deux codes d'état, 200 (normal) et 400 (erreur de validation). Spécifiez également le schéma de requête dans requestBody. Vous pouvez modifier le corps de la requête et accéder à l'API.

@api.route("/schema_driven")
async def schema_driven_view(req, resp):
    """exist id checker endpoint.
    ...
    ---
    post:
        description: Check the id exists or not
        requestBody:
            content:
                appliation/json:
                    schema:
                        $ref: "#/components/schemas/IdReqSchema"
        responses:
            200:
                description: true/false whether id exists to be returned
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/IsExistRespSchema"
            400:
                description: validation error
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/ErrorRespSchema"
    """

ezgif-1-396279e5f7b8.gif

Résumé

Introduction de la procédure de développement piloté par schéma avec le répondeur. Je suis très reconnaissant que Swagger puisse être vu avec cela seul. En plus de cela, responder a beaucoup de fonctionnalités très intéressantes, alors j'aimerais essayer différentes choses!

refs

Recommended Posts

Développement basé sur les schémas avec Responder: essayez d'afficher l'interface utilisateur Swagger
Essayez de défier le sol par récursif
Essayez d'afficher diverses informations utiles pour le débogage avec python
Essayez de profiler avec ONNX Runtime
Essayez d'afficher la carte google et la carte géographique avec python
Essayez de produire de l'audio avec M5 STACK
Essayez de reproduire un film couleur avec Python
Essayez de vous connecter à qiita avec Python
Exemple de script pour afficher BoundingBox avec PIL
Comment afficher le japonais python avec lolipop
Essayez de prédire les fleurs de cerisier avec XG Boost
Essayez de convertir en données ordonnées avec les pandas
Essayez rapidement de visualiser votre ensemble de données avec des pandas
Premier YDK à essayer avec Cisco IOS-XE
Exemple de programme pour afficher des vidéos avec PyQt
Essayez de générer une image avec aliénation
Essayez de créer votre propre AWS-SDK avec bash
Essayez de résoudre le problème du fizzbuzz avec Keras
Essayez de résoudre le diagramme homme-machine avec Python
Essayez d'extraire le document Azure document DB avec pydocumentdb
Essayez de dessiner une courbe de vie avec python
Je souhaite afficher plusieurs images avec matplotlib.
Comment essayer l'algorithme des amis d'amis avec pyfof
Essayez de créer un code de "décryptage" en Python
Essayez de générer automatiquement des documents Python avec Sphinx
Comment afficher des images en continu avec matplotlib Memo
Essayez de créer un groupe de dièdre avec Python
Essayez de rendre le client FTP le plus rapide avec Pythonista
Essayez de détecter les poissons avec python + OpenCV2.4 (inachevé)