Goed gestructureerde REST API's ontwerpen met Flask-RestPlus: Deel 1

Foto: Simone Viani @ Unsplash

Opmerking: dit artikel is binnenkort alleen beschikbaar op mijn blog. Voordat ik het van Medium verplaats, wil ik iedereen vriendelijk vragen om daar verdere opmerkingen en functieverzoeken te schrijven. Dank je!

Dit is het eerste deel van een tweedelige serie. In dit bericht zal ik Flask-RestPlus introduceren en demonstreren hoe API's rond de eenvoudige op REST gebaseerde conventies kunnen worden georganiseerd. De volgende keer zal ik ingaan op het onderwerp aanvraag / antwoord opstellen (serialisatie) en validatie.

Als ervaren Spring-ontwikkelaar voelde ik me een beetje ongemakkelijk bij het ontwerpen en toekomstbestendig maken van een op Flask gebaseerde API voor de eerste keer. Ik ben onlangs begonnen met het gebruik van Python, veel verder dan mijn oorspronkelijke bedoeling om gewoon met gegevens te spelen, en vond Flask een supergemakkelijk alternatief voor microservices voor Spring Boot of Ktor. Het enige waar ik me echt zorgen over maakte, was ervoor zorgen dat het API-verzoek / antwoord-formaat gestandaardiseerd was (denk aan een Swagger-schema), goed gedocumenteerd en gevalideerd. Tijdens het werken met Java zou veel hiervan rechtstreeks uit de compiler zelf komen, vanwege de statische aard van de taal. Wanneer u dit combineert met een paar geweldige bibliotheken zoals Jackson en SpringFox, wordt de API-communicatie gedocumenteerd en gevalideerd met minimale inbreuk op de werkelijke code. In Python zou dit vervelend zijn als anders overal wordt gecontroleerd ... dacht ik.

Flask-RestPlus voor de redding

In tegenstelling tot Django wordt Flask niet inclusief batterijen geleverd, maar er is een heel ecosysteem van open-source bibliotheken en uitbreidingen die door de gemeenschap worden bijgedragen. Een daarvan heet Flask-RestPlus en het is de absolute droom die uitkomt voor elke Flask API-ontwerper. Flask-RestPlus is een Flask-uitbreidingsbibliotheek en, zoals de naam al doet vermoeden, helpt het de bouw van gestructureerde RESTful API's met minimale installatie te vergemakkelijken en moedigt het best practices aan. Flask RestPlus volgt bepaalde conventies, maar dringt er niet op aan, zoals Django dat doet. In zekere zin probeert Flask-RestPlus een groeiend Flask-project te helpen organiseren, maar zonder de minimale overhead te verliezen, wat de grootste charme van Flask is.

Het doel van deze serie is om te beginnen met een eenvoudige Flask-app en proberen de volgende punten tegelijk aan te pakken met een beetje Flask-RestPlus:

  1. Een API structureren en automatisch documenteren (deel 1)
  2. Zorgen voor validatie van aanvraag / antwoord-payload (deel 2)

Demo-app

Laten we beginnen met een eenvoudige op een Flask gebaseerde API voor een conferentiebeheertoepassing:

uit kolf import Kolf

app = fles (__ naam__)


@ App.route ( "/ conferenties /")
def get_all__conferences ():
    """
    geeft een lijst met conferenties terug
    """


@ app.route ("/ conferenties /", methoden = ['POST'])
def add_conference ():
    """
    Voegt een nieuwe conferentie toe aan de lijst
    """


@ App.route ( "/ conferenties / ")
def get_conference (id):
    """
    Toont de details van een conferentie
    """
@ App.route ( "/ conferenties / ")
def edit_conference (id):
    """
    Bewerkt een geselecteerde conferentie
    """

Flask-RestPlus installeren is eenvoudig:

pip installeren Flask-RestPlus

Laten we nu gewoon een Api-object introduceren, proberen onze app-instantie ermee te verpakken, de routing-decorateurs vervangen en kijken wat er gebeurt:

uit kolf import Kolf
van flask_restplus import Api
app = fles (__ naam__)
api = Api (app = app)
@ Api.route ( "/ conferenties /")
def get_all__conferences ():
    """
    geeft een lijst met conferenties terug
    """
@ api.route ("/ conferenties /", methoden = ['POST'])
def add_conference ():
    """
    Voegt een nieuwe conferentie toe aan de lijst
    """
@ Api.route ( "/ conferenties / ")
def get_conference (id):
    """
    Toont de details van een conferentie
    """
@ Api.route ( "/ conferenties / ")
def edit_conference (id):
    """
    Bewerkt een geselecteerde conferentie
    """

Zodra de app start, krijgen we de volgende foutmelding:

AttributeError: 'function' object heeft geen attribuut 'as_view'

Dit komt omdat als u RestPlus voor sommige van uw Flask-functies wilt gebruiken, u deze in een scopingklasse moet opnemen. Niet alleen dat, maar binnen de omsluitende klasse, moet u uw methoden een naam geven, die overeenkomt met de HTTP-methoden waarop REST is gebaseerd: GET, POST, PUT en DELETE:

@ Api.route ( "/ conferenties /")
class ConferenceList (bron):
    def get (self):
        """
        geeft een lijst met conferenties terug
        """

Voordat iemand bezwaar gaat maken, moet ik uitleggen waarom dit nuttig is. Flask-RestPlus gebruikt het Flask-concept van “Pluggable Views” om Resource te introduceren (zoals in, REST-resource).

Laten we eerlijk zijn. Hoewel de meeste Flask-applicaties eenvoudig beginnen, ontgroeien veel van hen het oorspronkelijke idee en het proppen van verschillende handlerfuncties in de hoofdmodule wordt al snel een puinhoop. Dit is de reden waarom Flask Blueprints bestaan ​​om gemeenschappelijke functies in meerdere modules te splitsen.

Flask-RestPlus maakt ook veel gebruik van Blueprints, zoals ik later zal aantonen, maar bronnen gaan een niveau van granulariteit verder. Een Resource-klasse kan meerdere methoden hebben, maar elke methode moet worden genoemd naar een van de geaccepteerde HTTP-werkwoorden. Wat als u meer dan één GET- of POST-methode nodig heeft voor uw API? Maak goed meerdere Resource-klassen en plaats elke methode in de bijbehorende resource-klasse. In het begin lijkt het misschien een beetje overweldigend, afkomstig van de 'cut-the-boilerplate'-aard van Flask, maar met een beetje spelen, zal het helemaal geen brainer zijn en het zal op de lange termijn enorm lonen.

Laten we eens kijken hoe onze kleine app voor de transformaties zal zorgen:

uit kolf import Kolf
van flask_restplus import Api, Resource
app = fles (__ naam__)
api = Api (app = app)
@ Api.route ( "/ conferenties /")
class ConferenceList (bron):
    def get (self):
        """
        geeft een lijst met conferenties terug
        """
    def post (zelf):
        """
        Voegt een nieuwe conferentie toe aan de lijst
        """
@ Api.route ( "/ conferenties / ")
klassenconferentie (bron):
    def get (self, id):
        """
        Toont de details van een conferentie
        """
    def put (zelf, id):
        """
        Bewerkt een geselecteerde conferentie
        """

Met dit kleine beetje overhead (als u dit zelfs maar als overhead beschouwt) krijgt u er zoveel voor terug. Start de app en wijs naar http: // localhost: 5000. U zult zien dat de indexpagina is veranderd in een Swagger UI, die de reeds gedefinieerde API-eindpunten toont, netjes georganiseerd in categorieën (naamruimten):

Dit is geweldig om uw API-schema te documenteren, ermee te spelen en te delen. Toch is dit lang niet het enige dat Flask-RestPlus voor u doet. Het gaat verder dan alleen het documenteren van de API en zorgt ervoor dat de API voldoet aan het schema. Simpel gezegd, Flask-RestPlus zorgt ervoor dat als bepaalde aanvraagparameters als verplicht zijn gemarkeerd, of als aanvraag / antwoordmodellen een bepaalde structuur moeten hebben, deze tijdens runtime worden gecontroleerd en gevalideerd. Naar mijn mening is dit een echt voordeel van Flask-RestPlus, bovenop een Flask-toepassing. Het huidige voorbeeld is te eenvoudig om de werkelijke kracht van verzoek / antwoord opstellen en valideren aan te tonen, maar beide zullen grondig worden beschreven in deel 2.

namespaces

Naamruimten zijn optioneel en voegen een beetje extra organisatorisch tintje toe aan de API, vooral vanuit documentatie-oogpunt. Met een naamruimte kunt u gerelateerde bronnen onder een gemeenschappelijke root groeperen en is het eenvoudig om te maken:

ns_conf = api.namespace ('conferenties', description = 'Conferentiebewerkingen')

Om bepaalde bronnen onder een bepaalde naamruimte te brengen, volstaat het om @api te vervangen door @ns_conf. Merk ook op dat de naam van de naamruimte de naam van de resource vervangt, dus eindpunten kunnen eenvoudigweg verwijzen naar /, in plaats van de naam van de resource keer op keer te kopiëren:

uit kolf import Kolf
van flask_restplus import Api, Resource
app = fles (__ naam__)
api = Api (app = app)
ns_conf = api.namespace ('conferenties', description = 'Conferentiebewerkingen')
@ Ns_conf.route ( "/")
class ConferenceList (bron):
    def get (self):
        """
        geeft een lijst met conferenties terug
        """
    def post (zelf):
        """
        Voegt een nieuwe conferentie toe aan de lijst
        """
@ Ns_conf.route ( "/ ")
klassenconferentie (bron):
    def get (self, id):
        """
        Toont de details van een conferentie
        """
    def put (zelf, id):
        """
        Bewerkt een geselecteerde conferentie
        """

Men zal achteraf opmerken dat het Swagger UI-display ook is veranderd om de naamruimte weer te geven:

blauwdrukken

Flask Blueprints zijn een populaire manier om modulaire applicaties te ontwerpen. Hetzelfde geldt voor Flask-RestPlus. De productieversie van onze applicatie zal zeker de vier eindpunten ontgroeien waarmee we zijn begonnen. Er kunnen andere bronnen zijn, of u wilt op zijn minst uw API verplaatsen van de root van uw app. Beide gevallen zijn een perfecte kandidaat voor een blauwdruk. Laten we al onze API-eindpunten onder / api / v1 verplaatsen, zonder de routes van zelfs maar één ervan aan te raken. Dit voorbeeld komt rechtstreeks uit de Flask-RestPlus-documentatie en is illustratief genoeg om dit hoofdstuk van de reis af te sluiten:

Maak op de gebruikelijke manier een blauwdruk en in plaats van ons app-exemplaar te verpakken met de RestPlus API, zullen we in plaats daarvan de blauwdruk inpakken. Op deze manier kunnen we, onafhankelijk van onze app, ons API-onderdeel naar een andere module verplaatsen: (bijvoorbeeld blauwdruk / api.py)

van kolf import Blueprint
van flask_restplus import Api
blueprint = Blueprint ('api', __name__)
api = Api (blauwdruk)
# Breng hier de rest van onze API-code binnen

Dit laat slechts een klein beetje overbruggingscode over om de Blueprint in de hoofdapp te introduceren en het URL-voorvoegsel in te stellen. De volgende keer dat u uw app start, zijn de API-eindpunten alleen toegankelijk onder het opgegeven URL-voorvoegsel (/ api / v1).

uit kolf import Kolf
van apis importeer blauwdruk als api
app = fles (__ naam__)
app.register_blueprint (api, url_prefix = '/ api / 1')

Last but not least is het altijd een goed idee om de documentatie van de Swagger UI weg van de root te verplaatsen. Zoals in al het andere in RestPlus, is dit onderdeel ook extreem gemakkelijk. U kunt de standaardlocatie overschrijven door een extra parameter aan de initialisatie door te geven:

api = Api (app = app, doc = '/ docs')

Dit is het eerste deel van mijn serie. Ik hoop dat het informatief was en u zal helpen uw op Flask gebaseerde REST API's in de toekomst beter te structureren. Tot de volgende keer!

Verder lezen