# MVC 控制器与视图

# 创建 MVC 项目

yuanbootctl-python new mvc -n portal --framework-version 0.2.0
cd portal
python -m pip install -e '.[dev]'
yuanbootctl-python run --reload

访问 http://127.0.0.1:8080/ 查看 Jinja2 页面,访问 /docs 调试控制器接口。

# 控制器与业务服务

在 portal/controllers.py 中声明服务和控制器:

from pydantic import BaseModel, Field
from yuanboot.web import ApiController, ApiResult, controller, route


class GreetingRequest(BaseModel):
    name: str = Field(min_length=1, max_length=100)


class GreetingService:
    def greet(self, name: str) -> str:
        return f"Hello, {name}!"


@controller("/api", tags=["Greeting"])
class GreetingController(ApiController):
    def __init__(self, service: GreetingService):
        self.service = service

    @route("/greet", methods=["POST"])
    def greet(self, body: GreetingRequest) -> ApiResult[str]:
        return self.ok(self.service.greet(body.name))

控制器默认按请求创建,构造函数中的服务通过依赖注入解析。 动作参数由 FastAPI 绑定和校验;支持同步、异步方法及 FastAPI Depends。

在 portal/startup.py 中注册:

from .controllers import GreetingController, GreetingService


class Startup:
    def configure_services(self, services):
        services.add_scoped(GreetingService)

    def configure(self, app):
        app.use_mvc(lambda mvc: mvc.add_controller(GreetingController))

route 支持传递 FastAPI 路由参数,例如 status_code / response_model / dependencies。 self.ok(value) 返回统一的 ApiResult 响应。

# 渲染视图

Startup 中通过 app.use_views(directory) 注册模板服务。 可使用相对于模块的绝对路径,避免部署时工作目录变化影响模板定位:

from pathlib import Path

ROOT = Path(__file__).resolve().parent

# 在 Startup.configure 中调用:
# app.use_views(ROOT / "templates")

页面控制器通过依赖注入获取 Jinja2Templates:

from fastapi import Request
from starlette.responses import HTMLResponse
from starlette.templating import Jinja2Templates
from yuanboot.web import controller, route


@controller(tags=["Pages"])
class HomeController:
    def __init__(self, templates: Jinja2Templates):
        self.templates = templates

    @route("/", response_class=HTMLResponse)
    def index(self, request: Request):
        return self.templates.TemplateResponse(
            request=request,
            name="index.html",
            context={"title": "portal"},
        )

将 HomeController 一并加入 app.use_mvc,并在 portal/templates/index.html 中编写页面。 模板打包时应随业务包一起发布。