Design af velstrukturerede REST API'er med Flask-RestPlus: Del 1

Foto: Simone Viani @ Unsplash

Bemærk: Denne artikel vil snart være tilgængelig udelukkende på min blog. Før jeg flytter det fra Medium, vil jeg gerne bede alle om at skrive yderligere kommentarer og anmodninger om funktion der. Tak skal du have!

Dette er den første del af en todelt serie. I dette indlæg introducerer jeg Flask-RestPlus og demonstrerer, hvordan man begynder at organisere API'er omkring dets enkle REST-baserede konventioner. Næste gang vil jeg behandle emnet med anmodning / svarskontrol (serialisering) og validering.

En erfaren forårsudvikler følte jeg mig lidt urolig, da jeg designede og fremtidssikrede en kolbe-baseret API for første gang. Jeg er for nylig begyndt at bruge Python langt ud over min oprindelige intention om bare at lege med data og fandt, at Flask var et super-let mikrotjenestealternativ til Spring Boot eller Ktor. Den ene ting, jeg virkelig var bekymret for, var at sikre mig, at API-anmodning / svarformat var standardiseret (tænk, leverer et Swagger-skema), veldokumenteret og valideret. Mens jeg arbejder med Java, ville meget af dette komme lige fra selve kompilatoren på grund af sprogets statiske art. Når du kombinerer dette med et par gode biblioteker som Jackson og SpringFox, bliver API-kommunikationen dokumenteret og valideret med minimal indtrængen i den faktiske kode. I Python ville dette kræve kedelig hvis-ellers kontrol overalt ... eller sådan tænkte jeg.

Flask-RestPlus til redningen

I modsætning til Django kommer flasken ikke med batterier inkluderet, men der er et helt økosystem med open source-biblioteker og udvidelser, som samfundet bidrager med. En af disse kaldes Flask-RestPlus, og det er den absolutte drøm, der går i opfyldelse for enhver Flask API-designer. Flask-RestPlus er et Flask-udvidelsesbibliotek, og som navnet antyder hjælper det med at lette opbygningen af ​​strukturerede RESTful-API'er med minimal opsætning og tilskynder til bedste praksis. Flask RestPlus følger visse konventioner, men insisterer ikke på dem, som Django gør. På en måde prøver Flask-RestPlus at hjælpe med at organisere et voksende Flask-projekt, men uden at det mister sit minimale overhead, hvilket er Flaskes største charme.

Målet med denne serie er at starte med en simpel Flask-app og prøve at adressere følgende punkter med en smule Flask-RestPlus ad gangen:

  1. Strukturér og autodokumenter et API (del 1)
  2. Sørg for validering af anmodning / svar på nyttelast (del 2)

Demo-app

Lad os starte med et simpelt Flaskebaseret API til en konferenceadministrationsapplikation:

fra flaskeimport Flaske

app = kolbe (__ navn__)


@ App.route ( "/ konferencer /")
def get_all__ konferencer ():
    """
    returnerer en liste over konferencer
    """


@ app.route ("/ konferencer /", metoder = ['POST'])
def add_conference ():
    """
    Tilføjer en ny konference på listen
    """


@ App.route ( "/ konferencer / ")
def get_conference (id):
    """
    Viser en konferences detaljer
    """
@ App.route ( "/ konferencer / ")
def edit_conference (id):
    """
    Redigerer en valgt konference
    """

Det er let at installere Flask-RestPlus:

pip installer Flask-RestPlus

Lad os blot introducere et Api-objekt indtil videre, prøv at indpakke vores app-forekomst med det, udskift routingdekoratorerne og se, hvad der sker:

fra flaskeimport Flaske
fra flask_restplus import Api
app = kolbe (__ navn__)
api = Api (app = app)
@ Api.route ( "/ konferencer /")
def get_all__ konferencer ():
    """
    returnerer en liste over konferencer
    """
@ api.route ("/ konferencer /", metoder = ['POST'])
def add_conference ():
    """
    Tilføjer en ny konference på listen
    """
@ Api.route ( "/ konferencer / ")
def get_conference (id):
    """
    Viser en konferences detaljer
    """
@ Api.route ( "/ konferencer / ")
def edit_conference (id):
    """
    Redigerer en valgt konference
    """

Så snart appen starter, får vi følgende fejl:

AttributeError: 'funktion' objekt har ingen attribut 'as_view'

Dette skyldes, at hvis du vil bruge RestPlus til nogle af dine kolbefunktioner, skal du vedlægge dem i en scopingklasse. Ikke kun det, men inden for den lukkende klasse, skal du navngive dine metoder, svarende til de HTTP-metoder, som REST er baseret på: GET, POST, PUT og DELETE:

@ Api.route ( "/ konferencer /")
klassekonferenceListe (ressource):
    def få (selv):
        """
        returnerer en liste over konferencer
        """

Før nogen begynder at gøre indsigelse, lad mig forklare, hvorfor dette er nyttigt. Flask-RestPlus bruger flaskekonceptet "Pluggable Views" til at introducere ressource (som i REST-ressource).

Lad os være ærlige. Mens de fleste flaske-applikationer starter enkle, vokser mange af dem ud fra den oprindelige idé, og at proppe af flere håndteringsfunktioner i hovedmodulomfanget bliver hurtigt et rod. Dette er grunden til, at der findes flaskegrupper for at hjælpe med at opdele fælles funktionalitet i flere moduler.

Flask-RestPlus gør også stor brug af Blueprints, som jeg vil demonstrere senere, men Resources går et niveau af granularitet videre. En ressourceklasse kan have flere metoder, men hver enkelt skal navngives efter et af de accepterede HTTP-verb. Hvad hvis du har brug for mere end en GET- eller POST-metode til din API? Nå, opret flere ressourceklasser og sæt hver metode i den tilsvarende ressourceklasse. Det ser måske lidt overvældende ud fra flaskens afskårne kedelplade, men med lidt spil rundt vil det overhovedet ikke være en hjerne, og det vil betale sig enormt i det lange løb.

Lad os se, hvordan vores lille app ser efter transformationerne:

fra flaskeimport Flaske
fra flask_restplus import Api, ressource
app = kolbe (__ navn__)
api = Api (app = app)
@ Api.route ( "/ konferencer /")
klassekonferenceListe (ressource):
    def få (selv):
        """
        returnerer en liste over konferencer
        """
    def post (selv):
        """
        Tilføjer en ny konference på listen
        """
@ Api.route ( "/ konferencer / ")
klassekonference (ressource):
    def get (self, id):
        """
        Viser en konferences detaljer
        """
    def put (selv, id):
        """
        Redigerer en valgt konference
        """

Med denne lille smule overhead (hvis du overhovedet overvejer dette som nogen overhead), får du så meget til gengæld. Start appen, og peg på http: // localhost: 5000. Du vil se, at indekssiden er omdannet til en Swagger-brugergrænseflade, der viser de allerede definerede API-slutpunkter, pænt organiseret i kategorier (navneområder):

Dette er fantastisk til at dokumentere, lege med og dele dit API-skema omkring. Alligevel er dette langt ikke det eneste, Flask-RestPlus gør for dig. Det går ud over blot at dokumentere API'en ved at sikre, at API'en er i overensstemmelse med skemaet. Kort sagt, Flask-RestPlus sørger for, at hvis visse anmodningsparametre er markeret som obligatoriske, eller hvis anmodning / svarmodeller antages at have en bestemt struktur, kontrolleres og valideres disse under kørsel. Efter min mening er dette en reel fordel ved, at Flask-RestPlus sidder på toppen af ​​en Flask-applikation. Det nuværende eksempel er for simpelt til at demonstrere den virkelige magt ved anmodning / svar, der styres og valideres, men begge vil blive beskrevet grundigt i del 2.

namespaces

Navneflader er valgfri og tilføjer en smule ekstra organisatorisk touch til API, hovedsageligt fra et dokumentationsmæssigt synspunkt. Et navneområde giver dig mulighed for at gruppere relaterede ressourcer under en fælles rod og er enkel at oprette:

ns_conf = api.namespace ('konferencer', beskrivelse = 'Konferenceoperationer')

For at bringe visse ressourcer under et givet navneområde, er alt hvad du skal gøre, at erstatte @api med @ns_conf. Bemærk også, at navnet på navneområdet erstatter ressourceens navn, så endepunkter simpelthen kan henvise til / i stedet for at kopiere ressourceens navn gang på gang:

fra flaskeimport Flaske
fra flask_restplus import Api, ressource
app = kolbe (__ navn__)
api = Api (app = app)
ns_conf = api.namespace ('konferencer', beskrivelse = 'Konferenceoperationer')
@ Ns_conf.route ( "/")
klassekonferenceListe (ressource):
    def få (selv):
        """
        returnerer en liste over konferencer
        """
    def post (selv):
        """
        Tilføjer en ny konference på listen
        """
@ Ns_conf.route ( "/ ")
klassekonference (ressource):
    def get (self, id):
        """
        Viser en konferences detaljer
        """
    def put (selv, id):
        """
        Redigerer en valgt konference
        """

Man vil derefter bemærke, at Swagger UI-skærm også er ændret for at afspejle navneområdet:

blueprints

Kolbegrupper er en populær måde at designe modulopgaver. Det samme gælder for Flask-RestPlus. Produktionsversionen af ​​vores applikation vokser bestemt de fire slutpunkter, vi startede med. Der kan være andre ressourcer, eller i det mindste vil du måske flytte for at flytte din API væk fra roden til din app. Begge sager er en perfekt kandidat til en plan. Lad os flytte alle vores API-slutpunkter under / api / v1, uden at berøre ruterne for en af ​​dem. Dette eksempel kommer direkte fra Flask-RestPlus-dokumentationen og er illustrerende nok til at hjælpe med at lukke dette kapitel på rejsen:

Opret en Blueprint på den sædvanlige måde, og i stedet for at indpakke vores app-instans med RestPlus API, vil vi indpakke Blueprint i stedet. På denne måde er vi uafhængigt af vores app fri til at flytte vores API-del til et andet modul: (f.eks. Blueprint / api.py)

fra kolbeimport Blueprint
fra flask_restplus import Api
blueprint = Blueprint ('api', __name__)
api = Api (plan)
# Indsæt resten af ​​vores API-kode her

Dette efterlader kun en lille smule brokode for at introducere Blueprint til hovedappen og indstille URL-præfikset. Næste gang du starter din app, er API-slutpunkterne kun tilgængelige under det specificerede URL-præfiks (/ api / v1).

fra flaskeimport Flaske
fra apis import-planen som api
app = kolbe (__ navn__)
app.register_blueprint (api, url_prefix = '/ api / 1')

Sidst men ikke mindst er det altid en god ide at flytte dokumentationen til Swagger UI væk fra roden. Som i alt andet i RestPlus er denne del også ekstremt let. Du kan tilsidesætte standardplaceringen ved at videregive en ekstra parameter til initialiseringen:

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

Dette opsummerer den første del af min serie. Jeg håber, at det var informativt og vil hjælpe dig med at strukturere dine kolbebaserede REST-API'er bedre i fremtiden. Indtil næste gang!

Yderligere læsning