Controllers¶
Lilya embraces both functional and object-oriented programming (OOP) methodologies. Within the Lilya framework, the OOP paradigm is referred to as a controller, a nomenclature inspired by other notable technologies.
The Controller
serves as the orchestrator for handling standard HTTP requests and managing WebSocket sessions.
Internally, the Controller
and WebSocketController
implement the same response wrappers as the
Path and WebSocketPath making sure that remains
as one source of truth and that also means that the auto discovery of
the parameters also works here.
The Controller
class¶
This object also serves as ASGI application, which means that embraces the internal implementation
of the __call__
and dispatches the requests.
This is also responsible for implementing the HTTP dispatching of the requests, only.
from lilya.controllers import Controller
from lilya.requests import Request
from lilya.responses import Ok
class ASGIApp(Controller):
async def get(self, request: Request):
return Ok({"detail": "Hello, world!"})
async def post(self):
return Ok({"detail": "Hello, world!"})
When employing a Lilya application instance for routing management, you have the option to dispatch to a Controller
class.
Warning
It's crucial to dispatch directly to the class, not to an instance of the class.
Here's an example for clarification:
from lilya.apps import Lilya
from lilya.controllers import Controller
from lilya.responses import Ok, Response
from lilya.routing import Path
class ASGIAppController(Controller):
async def get(self):
return Response("Hello, World!")
class AuthController(Controller):
async def get(self, username: str):
return Ok({"message": f"Hello, {username}"})
app = Lilya(
routes=[
Path("/", handler=ASGIAppController),
Path("/{username}", handler=AuthController),
]
)
In this scenario, the ASGIApp
class is dispatched, not an instance of it.
Controller
classes, when encountering request methods that do not map to a corresponding handler,
will automatically respond with 405 Method Not Allowed
responses.
The WebSocketController
class¶
The WebSocketController
class serves as an ASGI application, encapsulating the functionality of a WebSocket
instance.
The ASGI connection scope is accessible on the endpoint instance through .scope
and features an attribute called encoding
.
This attribute, which may be optionally set, is utilized to validate the expected WebSocket data in the on_receive
method.
The available encoding types are:
'json'
'bytes'
'text'
There are three methods that can be overridden to handle specific ASGI WebSocket message types:
async def on_connect(websocket, **kwargs)
async def on_receive(websocket, data)
async def on_disconnect(websocket, close_code)
from typing import Any
from lilya.controllers import WebSocketController
from lilya.websockets import WebSocket
class ASGIApp(WebSocketController):
encoding = "bytes"
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
async def on_receive(self, websocket: WebSocket, data: Any):
await websocket.send_bytes(b"Message: " + data)
async def on_disconnect(self, websocket: WebSocket, close_code: int): ...
The WebSocketController
is also compatible with the Lilya application class.
from typing import Any
from lilya.apps import Lilya
from lilya.controllers import Controller, WebSocketController
from lilya.responses import HTMLResponse
from lilya.routing import Path, WebSocketPath
from lilya.websockets import WebSocket
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Submit</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://localhost:8000/websocket");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
class HomepageController(Controller):
async def get(self):
return HTMLResponse(html)
class EchoController(WebSocketController):
encoding = "text"
async def on_receive(self, websocket: WebSocket, data: Any):
await websocket.send_text(f"Message text was: {data}")
app = Lilya(
routes=[
Path("/", HomepageController),
WebSocketPath()("/websocket", EchoController),
]
)