Overview
ControllerResolver is responsible for discovering controller classes in your application, managing their lifecycle, and resolving their dependencies. It automatically scans your src/controller directory, caches controller classes, and instantiates them with proper dependency injection.
This component is used internally by Framefox’s routing system to resolve controller references and create controller instances when handling requests.
How It Works
The controller resolver follows this workflow:- Discovery: Scans
src/controllerdirectory for Python files containing controller classes - Registration: Maps controller names to their class definitions
- Caching: Stores controller classes for quick lookup
- Resolution: Resolves controller instances with dependency injection when needed
Controller Discovery
Naming Conventions
The resolver automatically discovers controllers based on these conventions:- File names: Any
.pyfile insrc/controller/(excluding__init__.py) - Class names: Classes ending with
Controller(e.g.,UserController,ArticleController) - Controller names: Generated by removing
Controllersuffix and converting to lowercaseUserController→userArticleController→articleAdminDashboardController→admindashboard
Directory Structure
The resolver recursively scans subdirectories:Constructor
__init__()
Initializes the controller resolver and automatically discovers all controllers.
- Initializes the service container for dependency injection
- Sets up logging for controller operations
- Creates an empty controller cache
- Discovers and registers all controllers in
src/controller
Methods
resolve_controller()
Resolves a controller by name and returns an instantiated controller object with all dependencies injected.
The name of the controller to resolve (without the “Controller” suffix, lowercase)
An instantiated controller object with all dependencies resolved
ControllerNotFoundError: If no controller with the given name existsControllerInstantiationError: If the controller cannot be instantiatedControllerDependencyError: If a required dependency cannot be resolved
- Checks if controller name exists in the name-to-class mapping
- Checks the controller cache for previously loaded classes
- If not cached, loads the controller class from the file system
- Resolves all constructor dependencies
- Instantiates and returns the controller
_discover_controller_paths() (Internal)
Scans the src/controller directory and builds a mapping of controller names to file paths.
Dictionary mapping controller names to their file paths
DuplicateControllerError: If multiple controllers with the same generated name are foundControllerModuleError: If a controller module cannot be imported
- Recursively scans
src/controllerfor.pyfiles - Imports each module and inspects for controller classes
- Generates controller names by removing “Controller” suffix
- Detects and prevents duplicate controller names
- Builds mappings for quick lookup
_load_controller_class() (Internal)
Loads a controller class from the file system by name.
The name of the controller to load
The controller class if found, None otherwise
_load_from_path() (Internal)
Loads a controller class from a specific file path.
The path to the Python file containing the controller
The controller class if found, None otherwise
ControllerModuleError: If the module cannot be imported
_create_controller_instance() (Internal)
Creates an instance of a controller class with dependency injection.
The controller class to instantiate
An instantiated controller object
ControllerInstantiationError: If instantiation fails
_resolve_controller_dependencies() (Internal)
Resolves constructor dependencies for a controller class using the service container.
The controller class to resolve dependencies for
List of resolved dependency instances in the order they appear in the constructor
ControllerDependencyError: If a required dependency cannot be resolved
- Inspects the controller’s
__init__signature - For each parameter (except
self):- If type-annotated, retrieves the dependency from the service container
- If has a default value, uses the default if retrieval fails
- Raises an error if required dependency cannot be resolved
- Returns list of resolved dependencies
Dependency Injection
The resolver automatically injects dependencies into controller constructors based on type annotations.Basic Example
resolve_controller('user') is called:
- The resolver inspects
UserController.__init__ - Finds type annotations:
UserServiceandUserRepository - Retrieves these services from the service container
- Instantiates:
UserController(user_service_instance, user_repo_instance)
Optional Dependencies
You can specify optional dependencies with default values:Caching
The resolver uses two levels of caching for performance:- Name-to-Class Mapping: Built during initial discovery
- Controller Cache: Stores loaded controller classes
Error Handling
The resolver provides detailed error messages for common issues:ControllerNotFoundError
Thrown when a controller cannot be found:DuplicateControllerError
Thrown during discovery if multiple controllers have the same name:ControllerModuleError
Thrown if a controller module has import errors:ControllerDependencyError
Thrown when a required dependency cannot be resolved:ControllerInstantiationError
Thrown if controller instantiation fails for any reason:Best Practices
1. Follow Naming Conventions
Always end controller class names withController:
2. Use Type Annotations for Dependencies
Always type-annotate constructor parameters:3. Avoid Name Collisions
Ensure controller names are unique across your application:4. Call super().init()
Always call the parent constructor when overriding__init__:
5. Register Dependencies in Service Container
Ensure all injected services are registered:Complete Example
Here’s a complete example showing controller resolution with dependency injection:See Also
- AbstractController - Learn about the base controller class and its methods
- Dependency Injection - Learn more about Framefox’s DI system
- Service Container - Learn how to register and configure services