Controllers
HTTP controllers are an intuitive way to organize the route handlers inside dedicated files, rather than using individual functions.
The controllers are stored within the ./app/http/controllers
directory,
each controller being isolated in a dedicated Python file. You may create a new controller by using
the make controller
command:
./beam make controller user
A newly created controller is simply a plain Python class to which you can add any number of methods that may be used as route handlers.
from expanse.view.view import View
from expanse.view.view_factory import ViewFactory
class UserController:
def index(self, view: ViewFactory) -> View:
return view.make(
"users/index", {
"users": [
{"id": 1, "username": "johndoe"},
{"id": 2, "username": "janedoe"},
]}
)
Now, we need to register this controller to the router. This can be done in any of your route files:
from expanse.routing.router import Router
def routes(router: Router) -> None:
from app.http.controllers.user import UserController
router.get("users", UserController.index)
One important thing to notice here is, even though index()
is an instance method,
the controller is not instantiated here but, instead, a direct reference to the method
is used: this is on purpose and allows Expanse to:
- Create a new instance of the controller for each request
- Create this new instance via the service container, allowing you to leverage automatic dependency injection in your controllers.
Dependency injection
Controller classes are created through the service container, which means you can type-hints any dependencies in the controller constructor for them to resolved automatically.
Let's say you manage your users with a UserRepository
class, you can inject an instance of it inside the controller
by simply type-hinting it:
from expanse.view.view import View
from expanse.view.view_factory import ViewFactory
from app.repositories.user import UserRepository
class UserController:
def __init__(self, repository: UserRepository) -> None:
self._repository = repository
def index(self, view: ViewFactory) -> View:
users = self._repository.get_all_users()
return view.make(
"users/index", {"users": users}
)
Method injection
Alternatively — or in addition — to the constructor injection, you can use method injection, just like you would use injection in function route handler:
from expanse.view.view import View
from expanse.view.view_factory import ViewFactory
from app.repositories.user import UserRepository
class UserController:
def index(self, repository: UserRepository, view: ViewFactory) -> View:
users = repository.get_all_users()
return view.make(
"users/index", {"users": users}
)