Falcon ist ein minimalistisches ASGI/WSGI-Framework zum Erstellen geschäftskritischer REST-APIs und Microservices mit Schwerpunkt auf Zuverlässigkeit, Korrektheit und Leistung im großen Maßstab.
Wenn es um die Erstellung von HTTP-APIs geht, erschweren andere Frameworks eine Menge Abhängigkeiten und unnötige Abstraktionen. Falcon bringt es auf den Punkt mit einem klaren Design, das HTTP und den REST-Architekturstil umfasst.
Falcon-Apps funktionieren mit jedem WSGI- oder ASGI-Server und laufen wie ein Champion unter CPython 3.8+ und PyPy 3.8+.
„Falcon ist grundsolide und schnell.“
„Wir haben Falcon als Ersatz für [ein anderes Framework] verwendet und wir lieben einfach die Leistung (dreimal schneller) und die Codebasisgröße (leicht die Hälfte unseres [ursprünglichen] Codes).“
„Ich liebe #falconframework! Super sauber und einfach, ich habe endlich die Geschwindigkeit und Flexibilität, die ich brauche!“
„Falcon sieht bisher großartig aus. Ich habe einen Schnelltest für einen meiner winzigen Server zusammengestellt und war mit nur 20 Minuten Arbeit etwa 40 % schneller.“
„Ich habe das Gefühl, dass ich endlich nur noch HTTP spreche, ohne dazwischen. Falcon scheint die Anfragen des Backends zu sein.“
„Der Quellcode für Falcon ist so gut, dass ich ihn fast der Dokumentation vorziehe. Im Grunde kann es nicht falsch sein.“
„Welches andere Framework bietet integrierte Unterstützung für 786? JETZT TESTEN?“
Falcon versucht, so wenig wie möglich zu tun und dabei dennoch hocheffektiv zu bleiben.
asyncio
UnterstützungHat Falcon Ihnen geholfen, eine tolle App zu erstellen? Zeigen Sie Ihre Unterstützung noch heute mit einer einmaligen Spende oder indem Sie Gönner werden. Unterstützer erhalten coole Ausrüstung, die Möglichkeit, ihre Marke bei Python-Entwicklern bekannt zu machen, und priorisierten Support.
Danke!
Vollkommenheit ist schließlich nicht dann erreicht, wenn es nichts mehr hinzuzufügen gibt, sondern wenn es nichts mehr wegzunehmen gibt.
- Antoine de Saint-Exupéry
Wir haben Falcon so konzipiert, dass es die anspruchsvollen Anforderungen großer Microservices und reaktionsfähiger App-Backends unterstützt. Falcon ergänzt allgemeinere Python-Web-Frameworks, indem es Bare-Metal-Leistung, Zuverlässigkeit und Flexibilität bietet, wo immer Sie sie benötigen.
Zuverlässig. Wir unternehmen große Anstrengungen, um die Einführung bahnbrechender Änderungen zu vermeiden. Wenn wir dies tun, werden diese vollständig dokumentiert und (im Sinne von SemVer) nur mit einem größeren Versionsinkrement eingeführt. Der Code wird anhand zahlreicher Eingaben gründlich getestet und wir fordern jederzeit eine 100-prozentige Abdeckung. Falcon hat keine Abhängigkeiten außerhalb der Standardbibliothek und trägt so dazu bei, die Angriffsfläche Ihrer App zu minimieren und gleichzeitig transitive Fehler und Breaking Changes zu vermeiden.
Debugbar. Falcon meidet Magie. Es ist leicht zu erkennen, welche Eingaben zu welchen Ausgaben führen. Nicht behandelte Ausnahmen werden niemals gekapselt oder maskiert. Potenziell überraschende Verhaltensweisen, wie etwa die automatische Analyse des Anforderungstexts, sind gut dokumentiert und standardmäßig deaktiviert. Schließlich achten wir beim Framework selbst darauf, die Logikpfade einfach und verständlich zu halten. All dies erleichtert das Nachdenken über den Code und das Debuggen von Randfällen in groß angelegten Bereitstellungen.
Schnell. Gleiche Hardware, mehr Anfragen. Falcon bearbeitet Anfragen deutlich schneller als andere beliebte Python-Frameworks wie Django und Flask. Für einen zusätzlichen Geschwindigkeitsschub kompiliert Falcon sich selbst mit Cython, sofern verfügbar, und funktioniert auch gut mit PyPy. Erwägen Sie einen Wechsel zu einer anderen Programmiersprache? Benchmarken Sie zuerst mit Falcon+PyPy!
Flexibel. Falcon überlässt Ihnen als API-Entwickler viele Entscheidungen und Implementierungsdetails. Dies gibt Ihnen viel Freiheit bei der Anpassung und Optimierung Ihrer Implementierung. Es hilft Ihnen auch, Ihre Apps auf einer tieferen Ebene zu verstehen, sodass sie auf lange Sicht einfacher optimiert, debuggt und umgestaltet werden können. Das minimalistische Design von Falcon bietet den Mitgliedern der Python-Community Raum für eigenständige Innovationen bei Falcon-Add-ons und ergänzenden Paketen.
Falcon wird weltweit von einer wachsenden Zahl von Organisationen genutzt, darunter:
Wenn Sie das Falcon-Framework für ein Community- oder kommerzielles Projekt verwenden, denken Sie bitte darüber nach, Ihre Informationen zu unserem Wiki unter „Wer verwendet Falcon?“ hinzuzufügen.
Zur Verwendung in Ihren Projekten stehen eine Reihe von Falcon-Add-ons, Vorlagen und ergänzenden Paketen zur Verfügung. Als Ausgangspunkt haben wir einige davon im Falcon-Wiki aufgelistet, Sie können aber auch in PyPI nach weiteren Ressourcen suchen.
Die Falconry-Community auf Gitter ist ein großartiger Ort, um Fragen zu stellen und Ihre Ideen auszutauschen. Sie finden uns in Falknerei/Benutzer. Wir haben auch einen Falknerei-/Entwicklungsraum, in dem wir das Design und die Entwicklung des Frameworks selbst besprechen können.
Gemäß unserem Verhaltenskodex erwarten wir von jedem, der an Community-Diskussionen teilnimmt, dass er professionell handelt und mit gutem Beispiel vorangeht und konstruktive Diskussionen anregt. Jeder Einzelne in der Gemeinschaft ist für die Schaffung einer positiven, konstruktiven und produktiven Kultur verantwortlich.
PyPy ist der schnellste Weg, Ihre Falcon-App auszuführen. PyPy3.8+ wird ab PyPy v7.3.7+ unterstützt.
$ pip install falcon
Oder um die neueste Beta oder den neuesten Release-Kandidaten zu installieren, falls vorhanden:
$ pip install --pre falcon
Falcon unterstützt auch CPython 3.8+ vollständig.
Die neueste stabile Version von Falcon kann direkt von PyPI installiert werden:
$ pip install falcon
Oder um die neueste Beta oder den neuesten Release-Kandidaten zu installieren, falls vorhanden:
$ pip install --pre falcon
Um die Geschwindigkeit zusätzlich zu steigern, kompiliert sich Falcon automatisch mit Cython unter jedem PEP 517-kompatiblen Installationsprogramm.
Für Ihren Komfort sind bei PyPI Räder mit vorkompilierten Binärdateien für die meisten gängigen Plattformen erhältlich. Selbst wenn kein Binär-Build für die Plattform Ihrer Wahl verfügbar ist, wählt pip
ein reines Python-Rad. Sie können Falcon auch für Ihre Umgebung cythonisieren; Weitere Informationen zu dieser und anderen erweiterten Optionen finden Sie in unseren Installationsdokumenten.
Falcon erfordert keine Installation weiterer Pakete.
Falcon spricht WSGI (oder ASGI; siehe auch unten). Um eine Falcon-App bereitzustellen, benötigen Sie einen WSGI-Server. Gunicorn und uWSGI gehören zu den beliebtesten, aber alles, was eine WSGI-App laden kann, reicht aus.
$ pip install [gunicorn | uwsgi]
Um eine Falcon ASGI-App bereitzustellen, benötigen Sie einen ASGI-Server. Uvicorn ist eine beliebte Wahl:
$ pip install uvicorn
Falcon lebt auf GitHub, wodurch der Code einfach durchsucht, heruntergeladen, gegabelt usw. werden kann. Pull-Anfragen sind immer willkommen! Bitte denken Sie auch daran, das Projekt zu markieren, wenn es Ihnen Freude macht. :) :)
Sobald Sie das Repo geklont oder einen Tarball von GitHub heruntergeladen haben, können Sie Falcon wie folgt installieren:
$ cd falcon
$ pip install .
Oder, wenn Sie den Code bearbeiten möchten, forken Sie zuerst das Haupt-Repo, klonen Sie den Fork auf Ihren Desktop und führen Sie dann Folgendes aus, um ihn mithilfe einer symbolischen Verknüpfung zu installieren, sodass die Änderungen automatisch verfügbar sind, wenn Sie Ihren Code ändern Ihre App, ohne das Paket neu installieren zu müssen:
$ cd falcon
$ FALCON_DISABLE_CYTHON=Y pip install -e .
Sie können Änderungen am Falcon-Framework manuell testen, indem Sie in das Verzeichnis des geklonten Repos wechseln und dann pytest ausführen:
$ cd falcon
$ pip install -r requirements/tests
$ pytest tests
Oder um die Standardtests auszuführen:
$ pip install tox && tox
Eine vollständige Liste der verfügbaren Umgebungen finden Sie auch in der Datei tox.ini.
Die Dokumentzeichenfolgen in der Falcon-Codebasis sind recht umfangreich und wir empfehlen, beim Erlernen des Frameworks eine REPL laufen zu lassen, damit Sie bei Fragen die verschiedenen Module und Klassen abfragen können.
Online-Dokumente sind verfügbar unter: https://falcon.readthedocs.io
Sie können dieselben Dokumente wie folgt lokal erstellen:
$ pip install tox && tox -e docs
Sobald die Dokumente erstellt wurden, können Sie sie anzeigen, indem Sie die folgende Indexseite in Ihrem Browser öffnen. Unter OS X ist es so einfach:
$ Öffnen Sie docs/_build/html/index.html
Oder unter Linux:
$ xdg-open docs/_build/html/index.html
Hier ist ein einfaches, erfundenes Beispiel, das zeigt, wie eine Falcon-basierte WSGI-App erstellt wird (die ASGI-Version ist weiter unten enthalten):
# examples/things.py
# Let's get this party started!
from wsgiref . simple_server import make_server
import falcon
# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource :
def on_get ( self , req , resp ):
"""Handles GET requests"""
resp . status = falcon . HTTP_200 # This is the default status
resp . content_type = falcon . MEDIA_TEXT # Default is JSON, so override
resp . text = ( ' n Two things awe me most, the starry sky '
'above me and the moral law within me. n '
' n '
' ~ Immanuel Kant n n ' )
# falcon.App instances are callable WSGI apps...
# in larger applications the app is created in a separate file
app = falcon . App ()
# Resources are represented by long-lived class instances
things = ThingsResource ()
# things will handle all requests to the '/things' URL path
app . add_route ( '/things' , things )
if __name__ == '__main__' :
with make_server ( '' , 8000 , app ) as httpd :
print ( 'Serving on port 8000...' )
# Serve until process is killed
httpd . serve_forever ()
Sie können das obige Beispiel direkt mit dem mitgelieferten wsgiref-Server ausführen:
$ pip install falcon
$ python things.py
Dann in einem anderen Terminal:
$ curl localhost:8000/things
Die ASGI-Version des Beispiels ist ähnlich:
# examples/things_asgi.py
import falcon
import falcon . asgi
# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource :
async def on_get ( self , req , resp ):
"""Handles GET requests"""
resp . status = falcon . HTTP_200 # This is the default status
resp . content_type = falcon . MEDIA_TEXT # Default is JSON, so override
resp . text = ( ' n Two things awe me most, the starry sky '
'above me and the moral law within me. n '
' n '
' ~ Immanuel Kant n n ' )
# falcon.asgi.App instances are callable ASGI apps...
# in larger applications the app is created in a separate file
app = falcon . asgi . App ()
# Resources are represented by long-lived class instances
things = ThingsResource ()
# things will handle all requests to the '/things' URL path
app . add_route ( '/things' , things )
Sie können die ASGI-Version mit uvicorn oder einem anderen ASGI-Server ausführen:
$ pip install falcon uvicorn
$ uvicorn things_asgi:app
Hier ist ein komplexeres Beispiel, das das Lesen von Headern und Abfrageparametern, den Umgang mit Fehlern und die Arbeit mit Anforderungs- und Antworttexten zeigt. Beachten Sie, dass in diesem Beispiel davon ausgegangen wird, dass das Anforderungspaket installiert wurde.
(Die entsprechende ASGI-App finden Sie unter: Ein komplexeres Beispiel (ASGI)).
# examples/things_advanced.py
import json
import logging
import uuid
from wsgiref import simple_server
import falcon
import requests
class StorageEngine :
def get_things ( self , marker , limit ):
return [{ 'id' : str ( uuid . uuid4 ()), 'color' : 'green' }]
def add_thing ( self , thing ):
thing [ 'id' ] = str ( uuid . uuid4 ())
return thing
class StorageError ( Exception ):
@ staticmethod
def handle ( ex , req , resp , params ):
# TODO: Log the error, clean up, etc. before raising
raise falcon . HTTPInternalServerError ()
class SinkAdapter :
engines = {
'ddg' : 'https://duckduckgo.com' ,
'y' : 'https://search.yahoo.com/search' ,
}
def __call__ ( self , req , resp , engine ):
url = self . engines [ engine ]
params = { 'q' : req . get_param ( 'q' , True )}
result = requests . get ( url , params = params )
resp . status = str ( result . status_code ) + ' ' + result . reason
resp . content_type = result . headers [ 'content-type' ]
resp . text = result . text
class AuthMiddleware :
def process_request ( self , req , resp ):
token = req . get_header ( 'Authorization' )
account_id = req . get_header ( 'Account-ID' )
challenges = [ 'Token type="Fernet"' ]
if token is None :
description = ( 'Please provide an auth token '
'as part of the request.' )
raise falcon . HTTPUnauthorized ( title = 'Auth token required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
if not self . _token_is_valid ( token , account_id ):
description = ( 'The provided auth token is not valid. '
'Please request a new token and try again.' )
raise falcon . HTTPUnauthorized ( title = 'Authentication required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
def _token_is_valid ( self , token , account_id ):
return True # Suuuuuure it's valid...
class RequireJSON :
def process_request ( self , req , resp ):
if not req . client_accepts_json :
raise falcon . HTTPNotAcceptable (
description = 'This API only supports responses encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
if req . method in ( 'POST' , 'PUT' ):
if 'application/json' not in req . content_type :
raise falcon . HTTPUnsupportedMediaType (
title = 'This API only supports requests encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
class JSONTranslator :
# NOTE: Normally you would simply use req.media and resp.media for
# this particular use case; this example serves only to illustrate
# what is possible.
def process_request ( self , req , resp ):
# req.stream corresponds to the WSGI wsgi.input environ variable,
# and allows you to read bytes from the request body.
#
# See also: PEP 3333
if req . content_length in ( None , 0 ):
# Nothing to do
return
body = req . stream . read ()
if not body :
raise falcon . HTTPBadRequest ( title = 'Empty request body' ,
description = 'A valid JSON document is required.' )
try :
req . context . doc = json . loads ( body . decode ( 'utf-8' ))
except ( ValueError , UnicodeDecodeError ):
description = ( 'Could not decode the request body. The '
'JSON was incorrect or not encoded as '
'UTF-8.' )
raise falcon . HTTPBadRequest ( title = 'Malformed JSON' ,
description = description )
def process_response ( self , req , resp , resource , req_succeeded ):
if not hasattr ( resp . context , 'result' ):
return
resp . text = json . dumps ( resp . context . result )
def max_body ( limit ):
def hook ( req , resp , resource , params ):
length = req . content_length
if length is not None and length > limit :
msg = ( 'The size of the request is too large. The body must not '
'exceed ' + str ( limit ) + ' bytes in length.' )
raise falcon . HTTPContentTooLarge (
title = 'Request body is too large' , description = msg )
return hook
class ThingsResource :
def __init__ ( self , db ):
self . db = db
self . logger = logging . getLogger ( 'thingsapp.' + __name__ )
def on_get ( self , req , resp , user_id ):
marker = req . get_param ( 'marker' ) or ''
limit = req . get_param_as_int ( 'limit' ) or 50
try :
result = self . db . get_things ( marker , limit )
except Exception as ex :
self . logger . error ( ex )
description = ( 'Aliens have attacked our base! We will '
'be back as soon as we fight them off. '
'We appreciate your patience.' )
raise falcon . HTTPServiceUnavailable (
title = 'Service Outage' ,
description = description ,
retry_after = 30 )
# NOTE: Normally you would use resp.media for this sort of thing;
# this example serves only to demonstrate how the context can be
# used to pass arbitrary values between middleware components,
# hooks, and resources.
resp . context . result = result
resp . set_header ( 'Powered-By' , 'Falcon' )
resp . status = falcon . HTTP_200
@ falcon . before ( max_body ( 64 * 1024 ))
def on_post ( self , req , resp , user_id ):
try :
doc = req . context . doc
except AttributeError :
raise falcon . HTTPBadRequest (
title = 'Missing thing' ,
description = 'A thing must be submitted in the request body.' )
proper_thing = self . db . add_thing ( doc )
resp . status = falcon . HTTP_201
resp . location = '/%s/things/%s' % ( user_id , proper_thing [ 'id' ])
# Configure your WSGI server to load "things.app" (app is a WSGI callable)
app = falcon . App ( middleware = [
AuthMiddleware (),
RequireJSON (),
JSONTranslator (),
])
db = StorageEngine ()
things = ThingsResource ( db )
app . add_route ( '/{user_id}/things' , things )
# If a responder ever raises an instance of StorageError, pass control to
# the given handler.
app . add_error_handler ( StorageError , StorageError . handle )
# Proxy some things to another service; this example shows how you might
# send parts of an API off to a legacy system that hasn't been upgraded
# yet, or perhaps is a single cluster that all data centers have to share.
sink = SinkAdapter ()
app . add_sink ( sink , r'/search/(?P<engine>ddg|y)Z' )
# Useful for debugging problems in your API; works with pdb.set_trace(). You
# can also use Gunicorn to host your app. Gunicorn can be configured to
# auto-restart workers when it detects a code change, and it also works
# with pdb.
if __name__ == '__main__' :
httpd = simple_server . make_server ( '127.0.0.1' , 8000 , app )
httpd . serve_forever ()
Auch dieser Code verwendet wsgiref, Sie können das obige Beispiel jedoch auch mit einem beliebigen WSGI-Server ausführen, z. B. uWSGI oder Gunicorn. Zum Beispiel:
$ pip install requests gunicorn
$ gunicorn things:app
Unter Windows können Sie Gunicorn und uWSGI über WSL ausführen, oder Sie könnten es mit Waitress versuchen:
$ pip install requests waitress
$ waitress-serve --port=8000 things:app
Um dieses Beispiel zu testen, öffnen Sie ein anderes Terminal und führen Sie Folgendes aus:
$ http localhost:8000/1/things authorization:custom-token
Sie können die Anwendungskonfiguration auch über die CLI über das im Framework enthaltene Skript falcon-inspect-app
anzeigen:
falcon-inspect-app things_advanced:app
Hier ist die ASGI-Version der App von oben. Beachten Sie, dass anstelle von Anfragen das httpx-Paket verwendet wird.
# examples/things_advanced_asgi.py
import json
import logging
import uuid
import falcon
import falcon . asgi
import httpx
class StorageEngine :
async def get_things ( self , marker , limit ):
return [{ 'id' : str ( uuid . uuid4 ()), 'color' : 'green' }]
async def add_thing ( self , thing ):
thing [ 'id' ] = str ( uuid . uuid4 ())
return thing
class StorageError ( Exception ):
@ staticmethod
async def handle ( ex , req , resp , params ):
# TODO: Log the error, clean up, etc. before raising
raise falcon . HTTPInternalServerError ()
class SinkAdapter :
engines = {
'ddg' : 'https://duckduckgo.com' ,
'y' : 'https://search.yahoo.com/search' ,
}
async def __call__ ( self , req , resp , engine ):
url = self . engines [ engine ]
params = { 'q' : req . get_param ( 'q' , True )}
async with httpx . AsyncClient () as client :
result = await client . get ( url , params = params )
resp . status = result . status_code
resp . content_type = result . headers [ 'content-type' ]
resp . text = result . text
class AuthMiddleware :
async def process_request ( self , req , resp ):
token = req . get_header ( 'Authorization' )
account_id = req . get_header ( 'Account-ID' )
challenges = [ 'Token type="Fernet"' ]
if token is None :
description = ( 'Please provide an auth token '
'as part of the request.' )
raise falcon . HTTPUnauthorized ( title = 'Auth token required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
if not self . _token_is_valid ( token , account_id ):
description = ( 'The provided auth token is not valid. '
'Please request a new token and try again.' )
raise falcon . HTTPUnauthorized ( title = 'Authentication required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
def _token_is_valid ( self , token , account_id ):
return True # Suuuuuure it's valid...
class RequireJSON :
async def process_request ( self , req , resp ):
if not req . client_accepts_json :
raise falcon . HTTPNotAcceptable (
description = 'This API only supports responses encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
if req . method in ( 'POST' , 'PUT' ):
if 'application/json' not in req . content_type :
raise falcon . HTTPUnsupportedMediaType (
description = 'This API only supports requests encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
class JSONTranslator :
# NOTE: Normally you would simply use req.get_media() and resp.media for
# this particular use case; this example serves only to illustrate
# what is possible.
async def process_request ( self , req , resp ):
# NOTE: Test explicitly for 0, since this property could be None in
# the case that the Content-Length header is missing (in which case we
# can't know if there is a body without actually attempting to read
# it from the request stream.)
if req . content_length == 0 :
# Nothing to do
return
body = await req . stream . read ()
if not body :
raise falcon . HTTPBadRequest ( title = 'Empty request body' ,
description = 'A valid JSON document is required.' )
try :
req . context . doc = json . loads ( body . decode ( 'utf-8' ))
except ( ValueError , UnicodeDecodeError ):
description = ( 'Could not decode the request body. The '
'JSON was incorrect or not encoded as '
'UTF-8.' )
raise falcon . HTTPBadRequest ( title = 'Malformed JSON' ,
description = description )
async def process_response ( self , req , resp , resource , req_succeeded ):
if not hasattr ( resp . context , 'result' ):
return
resp . text = json . dumps ( resp . context . result )
def max_body ( limit ):
async def hook ( req , resp , resource , params ):
length = req . content_length
if length is not None and length > limit :
msg = ( 'The size of the request is too large. The body must not '
'exceed ' + str ( limit ) + ' bytes in length.' )
raise falcon . HTTPContentTooLarge (
title = 'Request body is too large' , description = msg )
return hook
class ThingsResource :
def __init__ ( self , db ):
self . db = db
self . logger = logging . getLogger ( 'thingsapp.' + __name__ )
async def on_get ( self , req , resp , user_id ):
marker = req . get_param ( 'marker' ) or ''
limit = req . get_param_as_int ( 'limit' ) or 50
try :
result = await self . db . get_things ( marker , limit )
except Exception as ex :
self . logger . error ( ex )
description = ( 'Aliens have attacked our base! We will '
'be back as soon as we fight them off. '
'We appreciate your patience.' )
raise falcon . HTTPServiceUnavailable (
title = 'Service Outage' ,
description = description ,
retry_after = 30 )
# NOTE: Normally you would use resp.media for this sort of thing;
# this example serves only to demonstrate how the context can be
# used to pass arbitrary values between middleware components,
# hooks, and resources.
resp . context . result = result
resp . set_header ( 'Powered-By' , 'Falcon' )
resp . status = falcon . HTTP_200
@ falcon . before ( max_body ( 64 * 1024 ))
async def on_post ( self , req , resp , user_id ):
try :
doc = req . context . doc
except AttributeError :
raise falcon . HTTPBadRequest (
title = 'Missing thing' ,
description = 'A thing must be submitted in the request body.' )
proper_thing = await self . db . add_thing ( doc )
resp . status = falcon . HTTP_201
resp . location = '/%s/things/%s' % ( user_id , proper_thing [ 'id' ])
# The app instance is an ASGI callable
app = falcon . asgi . App ( middleware = [
# AuthMiddleware(),
RequireJSON (),
JSONTranslator (),
])
db = StorageEngine ()
things = ThingsResource ( db )
app . add_route ( '/{user_id}/things' , things )
# If a responder ever raises an instance of StorageError, pass control to
# the given handler.
app . add_error_handler ( StorageError , StorageError . handle )
# Proxy some things to another service; this example shows how you might
# send parts of an API off to a legacy system that hasn't been upgraded
# yet, or perhaps is a single cluster that all data centers have to share.
sink = SinkAdapter ()
app . add_sink ( sink , r'/search/(?P<engine>ddg|y)Z' )
Sie können die ASGI-Version mit jedem ASGI-Server ausführen, z. B. uvicorn:
$ pip install falcon httpx uvicorn
$ uvicorn things_advanced_asgi:app
Vielen Dank für Ihr Interesse an dem Projekt! Wir freuen uns über Pull-Anfragen von Entwicklern aller Erfahrungsstufen. Um zu beginnen, forken Sie einfach den Master-Zweig auf GitHub auf Ihr persönliches Konto und klonen Sie den Fork dann in Ihre Entwicklungsumgebung.
Wenn Sie einen Beitrag leisten möchten, aber noch nichts im Sinn haben, laden wir Sie ein, einen Blick auf die unter unserem nächsten Meilenstein aufgeführten Themen zu werfen. Wenn Sie eines sehen, an dem Sie gerne arbeiten würden, hinterlassen Sie bitte einen kurzen Kommentar, damit es nicht zu Doppelarbeit kommt. Dank im Voraus!
Bitte beachten Sie, dass alle Mitwirkenden und Betreuer dieses Projekts unserem Verhaltenskodex unterliegen.
Bevor Sie eine Pull-Anfrage senden, stellen Sie bitte sicher, dass Sie die entsprechenden Tests hinzugefügt/aktualisiert haben (und dass alle vorhandenen Tests mit Ihren Änderungen weiterhin bestanden werden) und dass Ihr Codierungsstil PEP 8 entspricht und keine Beschwerden von Pyflakes verursacht.
Commit-Nachrichten sollten nach AngularJS-Konventionen formatiert werden.
Kommentare folgen dem Styleguide von Google, mit der zusätzlichen Anforderung, Inline-Kommentaren Ihren GitHub-Nick und ein entsprechendes Präfix voranzustellen:
Die wichtigsten Betreuer des Falcon-Projekts sind:
Bitte zögern Sie nicht, uns zu kontaktieren, wenn Sie Fragen haben oder einfach nur ein wenig Hilfe beim Einstieg benötigen. Sie finden uns in falconry/dev auf Gitter.
Siehe auch: CONTRIBUTING.md
Copyright 2013–2024 bei individuellen und geschäftlichen Mitwirkenden, wie in den einzelnen Quelldateien angegeben.
Lizenziert unter der Apache-Lizenz, Version 2.0 (die „Lizenz“); Sie dürfen keinen Teil des Falcon-Frameworks verwenden, außer in Übereinstimmung mit der Lizenz. Mitwirkende erklären sich damit einverstanden, ihre Arbeit unter derselben Lizenz zu lizenzieren. Sie können eine Kopie der Lizenz unter http://www.apache.org/licenses/LICENSE-2.0 erhalten
Sofern nicht durch geltendes Recht vorgeschrieben oder schriftlich vereinbart, wird die im Rahmen der Lizenz vertriebene Software „WIE BESEHEN“ und OHNE GEWÄHRLEISTUNGEN ODER BEDINGUNGEN JEGLICHER ART, weder ausdrücklich noch stillschweigend, vertrieben. Die spezifische Sprache, die die Berechtigungen und Einschränkungen im Rahmen der Lizenz regelt, finden Sie in der Lizenz.