Ir para o conteúdo

Controladores

Lilya adopta tanto a metodologia de programação funcional quanto a orientada a objectos (OOP). Dentro da framework Lilya, o paradigma OOP é chamado de controller, uma nomenclatura inspirada noutras tecnologias notáveis.

O Controller actua como o orquestrador para lidar com pedidos HTTP padrão e gerir sessões WebSocket.

Internamente, o Controller e o WebSocketController implementam os mesmos wrappers de resposta que o Path e WebSocketPath, garantindo que permanece como uma única fonte de verdade e isso também significa que a descoberta automática dos parâmetros também funciona.

A classe Controller

Este objecto também serve como aplicação ASGI, o que significa que abraça a implementação interna do __call__ e despacha os pedidos.

Ele também é responsável por implementar apenas o despacho HTTP dos pedidos.

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!"})

Quando se utiliza uma instância da aplicação Lilya para gestão de roteamento, tem a opção de despachar para uma classe Controller.

Warning

É crucial o despacho directo para a class e não para uma instância.

Aqui está um exemplo para esclarecimento:

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),
    ]
)

Neste caso, a ASGIApp classe é despachada, não a instância da classe.

As classes Controller, ao encontrarem métodos de pedido que não correspondem a um manipulador correspondente, responderão automaticamente com respostas 405 Method not allowed.

A classe WebSocketController

A classe WebSocketController serve como uma aplicação ASGI, encapsulando a funcionalidade de uma instância WebSocket.

O âmbito da conecção ASGI é acessível na instância do endpoint através de .scope e possui um atributo chamado encoding. Esse atributo, que pode ser opcionalmente definido, é utilizado para validar os dados esperados do WebSocket no método on_receive.

Os tipos de codificação disponíveis são:

  • 'json'
  • 'bytes'
  • 'text' Existem três métodos que podem ser substituídos para lidar com tipos específicos de mensagens ASGI WebSocket:

  • 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): ...

O WebSocketController também é compatível com a classe da aplicação Lilya.

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),
    ]
)