Falcon은 규모에 따른 안정성, 정확성 및 성능에 중점을 두고 미션 크리티컬 REST API 및 마이크로서비스를 구축하기 위한 미니멀리스트 ASGI/WSGI 프레임워크입니다.
HTTP API를 구축할 때 다른 프레임워크는 수많은 종속성과 불필요한 추상화로 인해 부담을 줍니다. Falcon은 HTTP와 REST 아키텍처 스타일을 수용하는 깔끔한 디자인으로 추격에 나섰습니다.
Falcon 앱은 모든 WSGI 또는 ASGI 서버에서 작동하며 CPython 3.8+ 및 PyPy 3.8+에서 챔피언처럼 실행됩니다.
"팔콘은 견고하고 빠릅니다."
"우리는 [다른 프레임워크]를 대체하기 위해 Falcon을 사용해 왔으며 성능(3배 더 빠름)과 코드 기본 크기([원본] 코드의 거의 절반)가 마음에 듭니다."
"저는 #falconframework를 좋아합니다! 매우 깨끗하고 간단합니다. 마침내 필요한 속도와 유연성을 갖게 되었습니다!"
"Falcon은 현재까지 훌륭해 보입니다. 저는 작은 서버에 대한 빠른 테스트를 함께 해킹했고 단 20분의 작업만으로 최대 40% 더 빨라졌습니다."
"마침내 중간에 아무 것도 없이 그냥 HTTP에 대해 이야기하고 있는 것 같습니다. Falcon은 백엔드의 요청인 것 같습니다."
"Falcon의 소스 코드는 너무 훌륭해서 문서화보다 소스 코드를 더 선호합니다. 기본적으로 틀릴 리가 없습니다."
"786을 통합 지원하는 다른 프레임워크는 무엇입니까? 지금 사용해 보세요."
Falcon은 높은 효율성을 유지하면서 가능한 한 적은 작업을 수행하려고 합니다.
asyncio
비동기 지원Falcon이 멋진 앱을 만드는 데 도움이 되었나요? 오늘 일회성 기부나 후원자가 되어 지지를 보여주세요. 지지자는 멋진 장비, 자신의 브랜드를 Python 개발자에게 홍보할 수 있는 기회, 우선 지원을 받습니다.
감사해요!
완벽함은 더 이상 더할 것이 없을 때가 아니라 더 이상 뺄 것이 없을 때 비로소 달성됩니다.
- 앙투안 드 생텍쥐페리
우리는 대규모 마이크로서비스와 반응형 앱 백엔드의 까다로운 요구 사항을 지원하도록 Falcon을 설계했습니다. Falcon은 필요할 때마다 베어 메탈 성능, 안정성 및 유연성을 제공하여 보다 일반적인 Python 웹 프레임워크를 보완합니다.
믿을 수 있는. 우리는 주요 변경 사항을 도입하지 않기 위해 많은 노력을 기울이고, 그렇게 할 때 해당 사항은 완전히 문서화되고 주요 버전이 증가하는 방식으로만 (SemVer의 정신으로) 도입됩니다. 코드는 수많은 입력을 통해 엄격하게 테스트되었으며 항상 100% 적용이 필요합니다. Falcon은 표준 라이브러리 외부에 종속성이 없으므로 앱의 공격 표면을 최소화하는 동시에 일시적인 버그와 주요 변경 사항을 방지하는 데 도움이 됩니다.
디버깅 가능. 팔콘은 마법을 피합니다. 어떤 입력이 어떤 출력으로 이어지는지 쉽게 알 수 있습니다. 처리되지 않은 예외는 캡슐화되거나 마스크되지 않습니다. 자동 요청 본문 구문 분석과 같은 잠재적으로 놀라운 동작은 잘 문서화되어 있으며 기본적으로 비활성화되어 있습니다. 마지막으로, 프레임워크 자체에 관해서는 논리 경로를 단순하고 이해하기 쉽게 유지하도록 주의를 기울입니다. 이 모든 기능을 통해 코드에 대해 추론하고 대규모 배포에서 극단적인 사례를 디버그하는 것이 더 쉬워졌습니다.
빠른. 동일한 하드웨어, 더 많은 요청. Falcon은 Django 및 Flask와 같은 다른 인기 있는 Python 프레임워크보다 훨씬 빠르게 요청을 처리합니다. 추가 속도 향상을 위해 Falcon은 가능한 경우 Cython으로 자체 컴파일하고 PyPy에서도 잘 작동합니다. 다른 프로그래밍 언어로의 전환을 고려 중이신가요? 먼저 Falcon+PyPy로 벤치마크하세요!
유연한. Falcon은 많은 결정과 구현 세부 사항을 API 개발자에게 맡깁니다. 이는 구현을 사용자 정의하고 조정할 수 있는 많은 자유를 제공합니다. 또한 앱을 더 깊은 수준에서 이해하는 데 도움이 되므로 장기적으로 앱을 더 쉽게 조정하고 디버깅하고 리팩터링할 수 있습니다. Falcon의 미니멀한 디자인은 Python 커뮤니티 구성원이 Falcon 추가 기능 및 보완 패키지에 대해 독립적으로 혁신할 수 있는 공간을 제공합니다.
Falcon은 다음을 포함하여 점점 더 많은 조직에서 전 세계적으로 사용되고 있습니다.
커뮤니티 또는 상업 프로젝트를 위해 Falcon 프레임워크를 사용하는 경우 누가 Falcon을 사용하고 있습니까? 아래 위키에 정보를 추가하는 것을 고려해 보십시오.
다양한 Falcon 추가 기능, 템플릿 및 보완 패키지를 프로젝트에 사용할 수 있습니다. 우리는 시작점으로 Falcon 위키에 이들 중 몇 가지를 나열했지만 추가 리소스를 찾기 위해 PyPI를 검색할 수도 있습니다.
Gitter의 Falconry 커뮤니티는 질문을 하고 아이디어를 공유할 수 있는 좋은 장소입니다. 매사냥/사용자에서 우리를 찾을 수 있습니다. 또한 프레임워크 자체의 설계 및 개발을 논의하기 위한 매사냥/개발실도 있습니다.
우리의 행동 강령에 따라, 우리는 커뮤니티 토론에 참여하는 모든 사람이 전문적으로 행동하고 건설적인 토론을 장려하는 데 솔선수범할 것을 기대합니다. 지역사회의 각 개인은 긍정적이고 건설적이며 생산적인 문화를 조성할 책임이 있습니다.
PyPy는 Falcon 앱을 실행하는 가장 빠른 방법입니다. PyPy3.8+는 PyPy v7.3.7+부터 지원됩니다.
$ pip install falcon
또는 최신 베타 또는 릴리스 후보가 있는 경우 설치하려면 다음을 수행하세요.
$ pip install --pre falcon
Falcon은 CPython 3.8+도 완벽하게 지원합니다.
Falcon의 최신 안정 버전은 PyPI에서 직접 설치할 수 있습니다.
$ pip install falcon
또는 최신 베타 또는 릴리스 후보가 있는 경우 설치하려면 다음을 수행하세요.
$ pip install --pre falcon
추가 속도 향상을 제공하기 위해 Falcon은 PEP 517 호환 설치 프로그램에서 Cython으로 자동 컴파일됩니다.
귀하의 편의를 위해 대부분의 일반 플랫폼에 대해 사전 컴파일된 바이너리가 포함된 휠을 PyPI에서 사용할 수 있습니다. 선택한 플랫폼에 대한 바이너리 빌드를 사용할 수 없는 경우에도 pip
순수 Python 휠을 선택합니다. 귀하의 환경에 맞게 Falcon을 사이토화할 수도 있습니다. 이 옵션과 기타 고급 옵션에 대한 자세한 내용은 설치 문서를 참조하세요.
Falcon에는 다른 패키지를 설치할 필요가 없습니다.
Falcon은 WSGI(또는 ASGI, 아래 참조)를 사용합니다. Falcon 앱을 제공하려면 WSGI 서버가 필요합니다. Gunicorn과 uWSGI는 가장 인기 있는 앱 중 하나이지만 WSGI 앱을 로드할 수 있는 앱이라면 무엇이든 가능합니다.
$ pip install [gunicorn | uwsgi]
Falcon ASGI 앱을 제공하려면 ASGI 서버가 필요합니다. Uvicorn은 인기 있는 선택입니다.
$ pip install uvicorn
Falcon은 GitHub에 있으므로 코드 탐색, 다운로드, 포크 등이 쉽습니다. 끌어오기 요청은 언제나 환영합니다! 또한, 프로젝트가 마음에 드셨다면 별표를 표시해 주세요. :)
저장소를 복제하거나 GitHub에서 tarball을 다운로드한 후에는 다음과 같이 Falcon을 설치할 수 있습니다.
$ cd falcon
$ pip install .
또는 코드를 편집하려면 먼저 기본 저장소를 포크하고 포크를 데스크톱에 복제한 후 다음을 실행하여 기호 연결을 사용하여 설치합니다. 그러면 코드를 변경할 때 변경 사항이 자동으로 다음 사용자에게 제공됩니다. 패키지를 다시 설치하지 않고도 앱을 만들 수 있습니다.
$ cd falcon
$ FALCON_DISABLE_CYTHON=Y pip install -e .
복제된 저장소의 디렉터리로 전환한 다음 pytest를 실행하여 Falcon 프레임워크에 대한 변경 사항을 수동으로 테스트할 수 있습니다.
$ cd falcon
$ pip install -r requirements/tests
$ pytest tests
또는 기본 테스트 세트를 실행하려면 다음을 수행하세요.
$ pip install tox && tox
사용 가능한 전체 환경 목록은 tox.ini 파일을 참조하세요.
Falcon 코드 베이스의 독스트링은 매우 광범위하므로, 질문이 있을 때 다양한 모듈과 클래스를 쿼리할 수 있도록 프레임워크를 배우는 동안 REPL을 계속 실행하는 것이 좋습니다.
온라인 문서는 https://falcon.readthedocs.io에서 확인할 수 있습니다.
다음과 같이 동일한 문서를 로컬로 빌드할 수 있습니다.
$ pip install tox && tox -e docs
문서가 작성되면 브라우저에서 다음 색인 페이지를 열어 문서를 볼 수 있습니다. OS X에서는 다음과 같이 간단합니다.
$ docs/_build/html/index.html을 엽니다.
또는 Linux의 경우:
$ xdg-open docs/_build/html/index.html
다음은 Falcon 기반 WSGI 앱을 생성하는 방법을 보여주는 간단하고 인위적인 예입니다(ASGI 버전은 아래에 포함되어 있습니다).
# 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 ()
포함된 wsgiref 서버를 사용하여 위 예제를 직접 실행할 수 있습니다.
$ pip install falcon
$ python things.py
그런 다음 다른 터미널에서 다음을 수행합니다.
$ curl localhost:8000/things
예제의 ASGI 버전은 비슷합니다.
# 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 )
uvicorn 또는 다른 ASGI 서버를 사용하여 ASGI 버전을 실행할 수 있습니다.
$ pip install falcon uvicorn
$ uvicorn things_asgi:app
다음은 헤더 및 쿼리 매개변수 읽기, 오류 처리, 요청 및 응답 본문 작업을 보여주는 좀 더 자세한 예제입니다. 이 예에서는 요청 패키지가 설치되었다고 가정합니다.
(동등한 ASGI 앱은 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 ()
이 코드는 wsgiref를 사용하지만 uWSGI 또는 Gunicorn과 같은 WSGI 서버를 사용하여 위 예제를 실행할 수도 있습니다. 예를 들어:
$ pip install requests gunicorn
$ gunicorn things:app
Windows에서는 WSL을 통해 Gunicorn 및 uWSGI를 실행하거나 Waitress를 사용해 볼 수 있습니다.
$ pip install requests waitress
$ waitress-serve --port=8000 things:app
이 예제를 테스트하려면 다른 터미널을 열고 다음을 실행하세요.
$ http localhost:8000/1/things authorization:custom-token
프레임워크와 함께 번들로 제공되는 falcon-inspect-app
스크립트를 통해 CLI에서 애플리케이션 구성을 볼 수도 있습니다.
falcon-inspect-app things_advanced:app
위의 ASGI 버전 앱은 다음과 같습니다. 요청 대신 httpx 패키지를 사용한다는 점에 유의하세요.
# 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' )
uvicorn과 같은 ASGI 서버에서 ASGI 버전을 실행할 수 있습니다.
$ pip install falcon httpx uvicorn
$ uvicorn things_advanced_asgi:app
프로젝트에 관심을 가져주셔서 감사합니다! 우리는 모든 기술 수준의 개발자로부터 끌어오기 요청을 환영합니다. 시작하려면 GitHub의 마스터 브랜치를 개인 계정으로 포크한 다음 포크를 개발 환경에 복제하면 됩니다.
기여하고 싶지만 아직 염두에 둔 것이 없다면 다음 단계에 나열된 문제를 살펴보시기 바랍니다. 작업하고 싶은 작업이 있으면 중복 작업이 발생하지 않도록 빠른 댓글을 남겨주세요. 미리 감사드립니다!
이 프로젝트의 모든 기여자와 유지관리자는 우리 행동 강령의 적용을 받습니다.
끌어오기 요청을 제출하기 전에 적절한 테스트를 추가/업데이트했는지(그리고 기존의 모든 테스트가 변경 사항과 함께 통과하는지), 코딩 스타일이 PEP 8을 따르고 pyflakes가 불평을 일으키지 않는지 확인하세요.
커밋 메시지는 AngularJS 규칙을 사용하여 형식화되어야 합니다.
댓글은 Google의 스타일 가이드를 따르며, GitHub 닉네임과 적절한 접두사를 사용하여 인라인 댓글의 접두사를 지정해야 한다는 추가 요구 사항이 있습니다.
Falcon 프로젝트의 핵심 관리자는 다음과 같습니다:
질문이 있거나 시작하는 데 약간의 도움이 필요한 경우 주저하지 말고 문의해 주세요. Gitter의 falconry/dev에서 우리를 찾을 수 있습니다.
참조: CONTRIBUTING.md
개별 소스 파일에 명시된 대로 개인 및 기업 기여자에 의한 저작권 2013-2024.
Apache 라이센스 버전 2.0("라이센스")에 따라 라이센스가 부여되었습니다. 귀하는 라이선스를 준수하는 경우를 제외하고 Falcon 프레임워크의 어떤 부분도 사용할 수 없습니다. 기여자는 동일한 라이선스에 따라 자신의 저작물에 라이선스를 부여하는 데 동의합니다. http://www.apache.org/licenses/LICENSE-2.0에서 라이센스 사본을 얻을 수 있습니다.
해당 법률에서 요구하거나 서면으로 동의하지 않는 한, 라이선스에 따라 배포되는 소프트웨어는 명시적이든 묵시적이든 어떠한 종류의 보증이나 조건 없이 "있는 그대로" 배포됩니다. 라이선스에 따른 허가 및 제한 사항을 관리하는 특정 언어는 라이선스를 참조하세요.