Sie könnten eine API mit einer Pfadoperation erstellen, die einen Request an eine externe API auslösen könnte, welche von jemand anderem erstellt wurde (wahrscheinlich derselbe Entwickler, der Ihre API verwenden würde).
Der Vorgang, der stattfindet, wenn Ihre API-Anwendung die externe API aufruft, wird als „Callback“ („Rückruf“) bezeichnet. Denn die Software, die der externe Entwickler geschrieben hat, sendet einen Request an Ihre API und dann ruft Ihre API zurück (calls back) und sendet einen Request an eine externe API (die wahrscheinlich vom selben Entwickler erstellt wurde).
In diesem Fall möchten Sie möglicherweise dokumentieren, wie diese externe API aussehen sollte. Welche Pfadoperation sie haben sollte, welchen Body sie erwarten sollte, welche Response sie zurückgeben sollte, usw.
Sehen wir uns das alles anhand eines Beispiels an.
Stellen Sie sich vor, Sie entwickeln eine Anwendung, mit der Sie Rechnungen erstellen können.
Diese Rechnungen haben eine id, einen optionalen title, einen customer (Kunde) und ein total (Gesamtsumme).
Der Benutzer Ihrer API (ein externer Entwickler) erstellt mit einem POST-Request eine Rechnung in Ihrer API.
Dann wird Ihre API (beispielsweise):
die Rechnung an einen Kunden des externen Entwicklers senden.
das Geld einsammeln.
eine Benachrichtigung an den API-Benutzer (den externen Entwickler) zurücksenden.
Dies erfolgt durch Senden eines POST-Requests (von Ihrer API) an eine externe API, die von diesem externen Entwickler bereitgestellt wird (das ist der „Callback“).
Sehen wir uns zunächst an, wie die normale API-Anwendung aussehen würde, bevor wir den Callback hinzufügen.
Sie verfügt über eine Pfadoperation, die einen Invoice-Body empfängt, und einen Query-Parameter callback_url, der die URL für den Callback enthält.
Dieser Teil ist ziemlich normal, der größte Teil des Codes ist Ihnen wahrscheinlich bereits bekannt:
fromtypingimportUnionfromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:Union[str,None]=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:Union[HttpUrl,None]=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Tipp
Der Query-Parameter callback_url verwendet einen Pydantic-Url-Typ.
Das einzig Neue ist callbacks=invoices_callback_router.routes als Argument für den Pfadoperation-Dekorator. Wir werden als Nächstes sehen, was das ist.
Der möglicherweise wichtigste Teil des Callbacks besteht jedoch darin, sicherzustellen, dass Ihr API-Benutzer (der externe Entwickler) die externe API gemäß den Daten, die Ihre API im Requestbody des Callbacks senden wird, korrekt implementiert, usw.
Als Nächstes fügen wir den Code hinzu, um zu dokumentieren, wie diese externe API aussehen sollte, um den Callback von Ihrer API zu empfangen.
Diese Dokumentation wird in der Swagger-Oberfläche unter /docs in Ihrer API angezeigt und zeigt externen Entwicklern, wie diese die externe API erstellen sollten.
In diesem Beispiel wird nicht der Callback selbst implementiert (das könnte nur eine Codezeile sein), sondern nur der Dokumentationsteil.
Tipp
Der eigentliche Callback ist nur ein HTTP-Request.
Wenn Sie den Callback selbst implementieren, können Sie beispielsweise HTTPX oder Requests verwenden.
Schreiben des Codes, der den Callback dokumentiert¶
Dieser Code wird nicht in Ihrer Anwendung ausgeführt, wir benötigen ihn nur, um zu dokumentieren, wie diese externe API aussehen soll.
Sie wissen jedoch bereits, wie Sie mit FastAPI ganz einfach eine automatische Dokumentation für eine API erstellen.
Daher werden wir dasselbe Wissen nutzen, um zu dokumentieren, wie die externe API aussehen sollte ... indem wir die Pfadoperation(en) erstellen, welche die externe API implementieren soll (die, welche Ihre API aufruft).
Tipp
Wenn Sie den Code zum Dokumentieren eines Callbacks schreiben, kann es hilfreich sein, sich vorzustellen, dass Sie dieser externe Entwickler sind. Und dass Sie derzeit die externe API implementieren, nicht Ihre API.
Wenn Sie diese Sichtweise (des externen Entwicklers) vorübergehend übernehmen, wird es offensichtlicher, wo die Parameter, das Pydantic-Modell für den Body, die Response, usw. für diese externe API hingehören.
Erstellen Sie zunächst einen neuen APIRouter, der einen oder mehrere Callbacks enthält.
fromtypingimportUnionfromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:Union[str,None]=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:Union[HttpUrl,None]=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Um die Callback-Pfadoperation zu erstellen, verwenden Sie denselben APIRouter, den Sie oben erstellt haben.
Sie sollte wie eine normale FastAPI-Pfadoperation aussehen:
Sie sollte wahrscheinlich eine Deklaration des Bodys enthalten, die sie erhalten soll, z. B. body: InvoiceEvent.
Und sie könnte auch eine Deklaration der Response enthalten, die zurückgegeben werden soll, z. B. response_model=InvoiceEventReceived.
fromtypingimportUnionfromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:Union[str,None]=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:Union[HttpUrl,None]=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Es gibt zwei Hauptunterschiede zu einer normalen Pfadoperation:
Es muss kein tatsächlicher Code vorhanden sein, da Ihre Anwendung diesen Code niemals aufruft. Sie wird nur zur Dokumentation der externen API verwendet. Die Funktion könnte also einfach pass enthalten.
Der Pfad kann einen OpenAPI-3-Ausdruck enthalten (mehr dazu weiter unten), wo er Variablen mit Parametern und Teilen des ursprünglichen Requests verwenden kann, der an Ihre API gesendet wurde.
und sie würde eine Response von dieser externen API mit einem JSON-Body wie dem folgenden erwarten:
{"ok":true}
Tipp
Beachten Sie, dass die verwendete Callback-URL die URL enthält, die als Query-Parameter in callback_url (https://www.external.org/events) empfangen wurde, und auch die Rechnungs-id aus dem JSON-Body (2expen51ve).
An diesem Punkt haben Sie die benötigte(n) Callback-Pfadoperation(en) (diejenige(n), die der externe Entwickler in der externen API implementieren sollte) im Callback-Router, den Sie oben erstellt haben.
Verwenden Sie nun den Parameter callbacks im Pfadoperation-Dekorator Ihrer API, um das Attribut .routes (das ist eigentlich nur eine liste von Routen/Pfadoperationen) dieses Callback-Routers zu übergeben:
fromtypingimportUnionfromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:Union[str,None]=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:Union[HttpUrl,None]=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Tipp
Beachten Sie, dass Sie nicht den Router selbst (invoices_callback_router) an callback= übergeben, sondern das Attribut .routes, wie in invoices_callback_router.routes.