Integrating web frameworks with Asphalt
This library exists to enable easy integration of various web frameworks with Asphalt. To do this, the web application must be run as part of an Asphalt application.
There are two fundamental approaches to integrating web frameworks with Asphalt:
Pass a ready-made application object (usually with the routes defined already) to the framework-specific component provided here
Let the framework-specific component create the application object, and inject it into subcomponents as a resource, and add routes dynamically there (not possible with the
asgi
anddjango
components)
Practical examples
The two different methods are demonstrated in the following examples.
In static.py
, an application object is made and then handler for the root path is
added to it. Then it gets picked up by the framework specific component.
In dynamic.py
, a component gets declared instead. This component gets the
application injected into its start()
method, where the handler for the root path is
installed to the application.
Django is an exception to this, as it requires a specific project structure. For Django,
its views.py
module is presented here. The complete example can be found in the
repository. In particular it should be noted that the
Asphalt middleware (needed for resource injection) needs to be added to MIDDLEWARE
in the project’s settings.py
file.
async def application(scope, receive, send):
"""Trivial example of a raw ASGI 3.0 application without a framework."""
if scope["type"] == "http":
await receive()
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
],
}
)
await send(
{
"type": "http.response.body",
"body": b"Hello, world!",
"more_body": False,
}
)
services:
static:
component:
type: asgi3
app: static:application
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
application = FastAPI()
@application.get("/", response_class=PlainTextResponse)
async def root() -> str:
return "Hello, world!"
from asphalt.core import Component, Context, inject, resource
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
async def root() -> str:
return "Hello, world!"
class WebRootComponent(Component):
@inject
async def start(self, ctx: Context, app: FastAPI = resource()) -> None:
app.add_api_route("/", root, response_class=PlainTextResponse)
services:
static:
component:
type: fastapi
app: static:application
dynamic:
component:
type: fastapi
components:
myroot:
type: dynamic:WebRootComponent
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse, Response
application = Starlette()
@application.route("/")
async def root(request: Request) -> Response:
return PlainTextResponse("Hello, world!")
from asphalt.core import Component, Context, inject, resource
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse, Response
async def root(request: Request) -> Response:
return PlainTextResponse("Hello, world!")
class WebRootComponent(Component):
@inject
async def start(self, ctx: Context, app: Starlette = resource()) -> None:
app.add_route("/", root)
services:
static:
component:
type: starlette
app: static:application
dynamic:
component:
type: starlette
components:
myroot:
type: dynamic:WebRootComponent
from litestar import get
@get("/")
async def root() -> str:
return "Hello, world!"
from asphalt.core import Component, Context, require_resource
from litestar import Litestar, get
@get("/")
async def root() -> str:
return "Hello, world!"
class WebRootComponent(Component):
async def start(self, ctx: Context) -> None:
require_resource(Litestar).register(root)
services:
static:
component:
type: litestar
route_handlers: ["static:root"]
dynamic:
component:
type: litestar
components:
myroot:
type: dynamic:WebRootComponent
from django.http import HttpRequest, HttpResponse
async def index(request: HttpRequest) -> HttpResponse:
return HttpResponse(b"Hello, world!")
services:
static:
component:
type: django
app: django_example.asgi:application
from aiohttp.abc import Request
from aiohttp.web_app import Application
from aiohttp.web_response import Response
from aiohttp.web_routedef import RouteTableDef
routes = RouteTableDef()
@routes.get("/")
async def root(request: Request) -> Response:
return Response(text="Hello, world!")
application = Application()
application.add_routes(routes)
from aiohttp.abc import Request
from aiohttp.web_app import Application
from aiohttp.web_response import Response
from asphalt.core import Component, Context, inject, resource
async def root(request: Request) -> Response:
return Response(text="Hello, world!")
class WebRootComponent(Component):
@inject
async def start(self, ctx: Context, app: Application = resource()) -> None:
app.router.add_route("GET", "/", root)
services:
static:
component:
type: aiohttp
app: static:application
dynamic:
component:
type: aiohttp
components:
myroot:
type: dynamic:WebRootComponent
To run these examples, copy all files to the same directory, and then (assuming
asphalt-web
and the appropriate web framework itself are installed):
PYTHONPATH=. asphalt run config.yaml --service static
or, for the dynamic
alternative (where available):
PYTHONPATH=. asphalt run config.yaml --service dynamic
Injecting resources to handler functions
In most cases, dependency injection works the same with request handler functions: you
decorate the function with @inject
and add one or more type annotated arguments with
resource()
as the default. One framework – FastAPI – requires special measures,
however. See the FastAPI section for details.
Adding middleware
All applications will be automatically wrapped by the Asphalt middleware, but you can add your own middleware on top of that. All frameworks supported here (except AIOHTTP) can be wrapped with ASGI 3.0 middleware, while AIOHTTP will need its own specific kind of middleware.
Note
The application resource available on the global context is the unwrapped application, and is unaffected by middleware.