ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Yii 2 控制器(Controller)完全指南:路由、Action 与请求处理生命周期

Yii 2 控制器(Controller)完全指南:路由、Action 与请求处理生命周期 后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载本篇指南以 Yii 2 官方指南的控制器章节为骨架系统讲解控制器在 MVC 架构 中的职责、路由Route的解析规则、Controller 与 Action 的创建规范、Action 参数绑定机制以及控制器完整生命周期。读完本文你将能够正确命名与组织控制器和 Action、通过 controllerMap 接入第三方控制器、按需选用 Inline/Standalone Action并深入理解请求从进入应用到产出响应的每一步内部机制。文章中的代码示例可直接复制运行源码级证据均来自本仓库的 framework 目录。控制器在 MVC 中的角色控制器是 MVC 设计模式 的一部分。在 Yii 2 中控制器是继承自yii\base\Controller的类对象见 framework/base/Controller.php其职责是处理请求并生成响应。具体来说当控制器从应用接过控制权后会依次完成以下工作分析传入的请求数据将数据交给模型处理业务逻辑把模型的处理结果注入视图最终生成对外响应。也就是说控制器是模型与视图之间的调度中枢它自己不做业务计算也不直接输出 HTML而是协调两者完成一次完整的请求-响应闭环。Actions用户可寻址的最小执行单元控制器由Action动作组成。Action 是终端用户可以直接寻址并要求执行的最小单元一个控制器可以包含一个或多个 Action。下面的示例定义了一个post控制器包含view和create两个 Actionnamespace app\controllers; use Yii; use app\models\Post; use yii\web\Controller; use yii\web\NotFoundHttpException; class PostController extends Controller { public function actionView($id) { $model Post::findOne($id); if ($model null) { throw new NotFoundHttpException; } return $this-render(view, [ model $model, ]); } public function actionCreate() { $model new Post; if ($model-load(Yii::$app-request-post()) $model-save()) { return $this-redirect([view, id $model-id]); } else { return $this-render(create, [ model $model, ]); } } }viewAction由actionView()方法定义先按请求中的模型 ID 加载模型加载成功则通过名为view的视图展示数据否则抛出NotFoundHttpException404异常。createAction由actionCreate()方法定义先尝试用请求数据填充一个新的模型实例并保存若保存成功则将浏览器重定向到viewAction 并携带新模型的 ID否则渲染create视图让用户填写输入表单。路由Routes如何寻址到 Action终端用户通过所谓的route路由来寻址 Action。一个路由字符串由以下部分组成模块 IDmodule ID仅当控制器属于某个非应用级模块时才存在控制器 ID在同级应用或同一模块内唯一标识一个控制器的字符串Action ID在同一控制器内唯一标识一个 Action 的字符串。路由的基本格式为ControllerID/ActionID若控制器属于某个模块则格式为ModuleID/ControllerID/ActionID例如当用户请求https://hostname/index.php?rsite/index时site控制器中的indexAction 将被执行。关于路由如何进一步被解析为具体 Action 的完整细节请参考路由与 URL 生成章节。从源码层面看路由到控制器的解析过程在yii\base\Module::createController()见 framework/base/Module.php中实现其算法依次为路由为空时使用defaultRoute路由首段命中controllerMap时按其中的配置创建控制器路由首段是已声明的模块 ID 时将剩余部分交给该模块的createController()递归解析否则在controllerNamespace下尝试abc\DefController或abc\def\XyzController两种类名形态。最终Module::runAction()见 framework/base/Module.php拿到控制器与剩余 Action ID 后调用Controller::runAction()执行。创建控制器在 Web 应用 中控制器应继承yii\web\Controller或其子类同理在 console 应用 中控制器应继承yii\console\Controller或其子类。下面的代码定义了一个site控制器namespace app\controllers; use yii\web\Controller; class SiteController extends Controller { }控制器 IDController ID通常一个控制器专为处理某一类特定资源的请求而设计因此控制器 ID 常采用名词指代它所处理的资源类型。例如用article作为处理文章数据的控制器 ID。默认情况下控制器 ID 只能包含以下字符小写英文字母、数字、下划线、连字符-和正斜杠/。例如article、post-comment都是合法 ID而article?、PostComment、admin\post均不合法。控制器 ID 还可以包含子目录前缀。例如admin/article表示在controllerNamespace控制器命名空间下的admin子目录中名为article的控制器。子目录前缀允许的字符包括大小写英文字母、数字、下划线和正斜杠其中正斜杠用于分隔多级子目录例如panels/admin。在源码中这一约束由Module::createControllerByID()中的isIncorrectClassNameOrPrefix()检查保证见 framework/base/Module.php。控制器类命名Controller Class Naming控制器类名按以下步骤从控制器 ID 推导将 ID 中每个以连字符分隔的单词首字母转为大写注意若 ID 包含斜杠该规则只作用于最后一个斜杠之后的部分删除连字符并把正斜杠替换为反斜杠命名空间分隔符追加后缀Controller前面加上controllerNamespace。假设controllerNamespace取默认值app\controllers该默认值定义于 framework/base/Application.php则控制器 ID推导出的类名articleapp\controllers\ArticleControllerpost-commentapp\controllers\PostCommentControlleradmin/post-commentapp\controllers\admin\PostCommentControlleradminPanels/post-commentapp\controllers\adminPanels\PostCommentController控制器类必须可自动加载。因此在上述示例中article控制器类应存放在别名app/controllers/ArticleController.php对应的文件里admin/post-comment则应存放在app/controllers/admin/PostCommentController.php。提示admin/post-comment这个例子展示了如何把控制器放进controllerNamespace的子目录。当你想按分类组织控制器、又不想引入模块机制时这个用法非常实用。在源码实现层面createControllerByID()使用正则%-([a-z0-9_])%i配合ucfirst完成连字符转大写步骤最终拼接出className . Controller并校验类是否存在、是否继承自yii\base\Controller见 framework/base/Module.php。Controller Map突破命名约束可以通过配置controllerMap控制器映射来克服上述控制器 ID 与类名之间的推导约束。当你使用第三方控制器、无法控制其类名时这一机制尤其有用。在应用配置中配置controllerMap即可例如[ controllerMap [ // 用类名声明 account 控制器 account app\controllers\UserController, // 用配置数组声明 article 控制器 article [ class app\controllers\PostController, enableCsrfValidation false, ], ], ]这里的值可以是纯类名也可以是标准配置数组。配置数组形式允许你顺带设置控制器属性——例如上面例子中把article控制器的enableCsrfValidation关闭该属性定义于 framework/web/Controller.php详见下文 CSRF 小节。在源码中controllerMap是yii\base\Module的公开属性见 framework/base/Module.php并在createController()中优先于模块和默认类名推导被检查见 framework/base/Module.php。默认控制器Default Controller每个应用都有一个通过defaultRoute属性指定的默认控制器。当请求没有显式指定路由时将使用该属性指定的路由。Web 应用的默认值是site见 framework/web/Application.phpconsole 应用的默认值是help见 framework/console/Application.php。因此当访问https://hostname/index.php时site控制器将处理该请求。可以在应用配置中修改默认控制器[ defaultRoute main, ]创建 Actions创建 Action 非常简单在控制器类中定义所谓的action 方法即可。Action 方法是一个以action开头的public方法其返回值代表要发送给终端用户的响应数据。下面的代码定义了index和hello-world两个 Actionnamespace app\controllers; use yii\web\Controller; class SiteController extends Controller { public function actionIndex() { return $this-render(index); } public function actionHelloWorld() { return Hello World; } }Action IDAction ID一个 Action 通常针对资源的某一种特定操作而设计因此 Action ID 多为动词例如view、update等。默认情况下Action ID 只能包含小写英文字母、数字、下划线和连字符连字符用于连接单词。例如view、update2、comment-post都合法而view?和Update不合法。创建 Action 有两种方式Inline Actions内联动作和Standalone Actions独立动作。Inline Action 以控制器类中的方法形式定义Standalone Action 则是继承yii\base\Action或其子类的类。如果你不打算复用该 ActionInline 方式更省力、也最常用而 Standalone Action 主要用于在不同的控制器中复用或以扩展形式对外分发。Inline Actions内联动作Inline Action 即上面描述的以方法形式定义的 Action。方法名按以下步骤从 Action ID 推导将 ID 中每个单词的首字母转为大写删除连字符加上前缀action。例如index→actionIndexhello-world→actionHelloWorld。注意Action 方法名是区分大小写的。如果你定义了ActionIndex这样的方法它不会被当作 Action 方法此时请求indexAction 将抛出异常。另外Action 方法必须是public的private或protected方法不会定义出 Inline Action。这一点在源码中有严格对应Controller::createAction()首先用正则/^(?:[a-z0-9_]-)*[a-z0-9_]$/校验 Action ID然后通过ucwords与str_replace生成方法名并用ReflectionMethod检查该方法是否为 public 且名称完全一致只有全部通过才会创建InlineAction见 framework/base/Controller.php。InlineAction本身只记录控制器与方法名运行时通过call_user_func_array调用见 framework/base/InlineAction.php。Standalone Actions独立动作Standalone Action 通过继承yii\base\Action或其子类的 Action 类来定义。在 Yii 发行版中yii\web\ViewAction和yii\web\ErrorAction都是 Standalone Action 的典型实例。要使用 Standalone Action需要在控制器类中重写actions()方法在action map动作映射里声明它public function actions() { return [ // 用类名声明 error 动作 error yii\web\ErrorAction, // 用配置数组声明 view 动作 view [ class yii\web\ViewAction, viewPrefix , ], ]; }可以看到actions()返回一个数组键为 Action ID值为对应的 Action 类名或配置数组。与 Inline Action 不同Standalone Action 的 ID可以包含任意字符只要它在actions()中声明即可。基类中actions()的默认实现返回空数组见 framework/base/Controller.phpcreateAction()会优先在 action map 中查找 ID命中则通过Yii::createObject()创建见 framework/base/Controller.php。要创建 Standalone Action 类应继承yii\base\Action或其子类并实现一个名为run()的公开方法。run()的作用与 Action 方法类似。例如?php namespace app\components; use yii\base\Action; class HelloWorldAction extends Action { public function run() { return Hello World; } }Action::runWithParams()会校验run()方法是否存在然后委托控制器完成参数绑定后再调用见 framework/base/Action.php。Action 的返回值Action ResultsAction 方法或 Standalone Action 的run()方法的返回值至关重要它代表了该 Action 的执行结果。返回值可以是一个响应对象该对象将被作为响应直接发送给终端用户对于 Web 应用返回值也可以是任意数据这些数据会被赋值给yii\web\Response::$data再进一步转换为表示响应体的字符串对于 console 应用返回值还可以是一个整数代表命令执行的退出状态码。前面示例中的返回值都是字符串会被当作发送给用户的响应体。下面的示例展示了一个 Action 如何通过返回响应对象因为redirect()方法返回的就是响应对象将用户浏览器重定向到新 URLpublic function actionForward() { // 将用户浏览器重定向到 https://example.com return $this-redirect(https://example.com); }redirect()方法定义于 framework/web/Controller.php是Response::redirect()的快捷方式支持字符串 URL、路径别名以及[route, param value]数组三种形式默认使用 302 状态码。同类的便捷方法还有goHome()、goBack()、refresh()以及用于返回 JSON/XML 响应的asJson()/asXml()见 framework/web/Controller.php。Action 参数Action ParametersInline Action 的方法和 Standalone Action 的run()方法都可以接收参数这些参数被称为action 参数其值来自请求。对于 Web 应用每个 Action 参数的值以参数名作为键从$_GET中获取对于 console 应用则对应命令行参数。下面的示例中viewActionInline Action声明了两个参数$id和$versionnamespace app\controllers; use yii\web\Controller; class PostController extends Controller { public function actionView($id, $version null) { // ... } }不同请求下 Action 参数的填充结果如下请求 URL参数填充结果https://hostname/index.php?rpost/viewid123$id为123$version因没有version参数仍为nullhttps://hostname/index.php?rpost/viewid123version2$id与$version分别填充为123与2https://hostname/index.php?rpost/view因必需的$id参数缺失抛出yii\web\BadRequestHttpExceptionhttps://hostname/index.php?rpost/viewid[]123因$id收到意外的数组值[123]抛出yii\web\BadRequestHttpException如果你希望 Action 参数接受数组值可以为其加上array类型声明public function actionView(array $id, $version null) { // ... }此时若请求为https://hostname/index.php?rpost/viewid[]123$id将取值为[123]若请求为https://hostname/index.php?rpost/viewid123$id仍会收到同样的数组值因为标量值123会被自动转换为数组。上述示例主要展示 Web 应用中 Action 参数的工作方式。console 应用的参数机制请参考控制台命令章节。从源码看参数绑定由yii\web\Controller::bindActionParams()实现见 framework/web/Controller.php它通过反射遍历方法参数对每个参数依次尝试从请求中取值 → 按类型校验/过滤 → 取默认值 → 记为缺失。当参数带有array类型声明时任何输入都会被强制转换为数组见filterSingleTypeActionParam()framework/web/Controller.php若参数声明了int、float、bool等标量类型则会通过filter_var进行校验与转换见 framework/web/Controller.php缺失的必需参数会汇总后统一抛出BadRequestHttpException。此外从 2.0.36 开始未在请求中出现的、类型为类/接口的参数还会走依赖注入绑定逻辑bindInjectedParams()见 framework/base/Controller.php这是从源码结构中可以确认的扩展能力。默认 ActionDefault Action每个控制器都有一个通过defaultAction属性指定的默认 Action。当路由只包含控制器 ID 时即表示请求该控制器的默认 Action。默认情况下默认 Action 为index。如果想改变默认值直接在控制器类中重写该属性即可该属性定义于 framework/base/Controller.phpnamespace app\controllers; use yii\web\Controller; class SiteController extends Controller { public $defaultAction home; public function actionHome() { return $this-render(home); } }控制器生命周期Controller Lifecycle在处理请求时应用会根据请求的路由创建控制器实例然后控制器经历以下生命周期来完成请求初始化控制器创建并完成配置后调用Controller::init()方法。基类中init()负责将request与response属性解析为对应的应用组件实例见 framework/base/Controller.php。创建 Action 对象控制器根据请求的 Action ID 创建动作对象若 Action ID 未指定使用默认 Action IDdefaultAction若 Action ID 在actions()返回的 action map 中命中则创建 Standalone Action若 Action ID 与某个 action 方法匹配则创建 Inline Action否则抛出yii\base\InvalidRouteException异常。beforeAction 阶段控制器依次调用应用、模块若控制器属于某模块、控制器自身的beforeAction()方法若其中一次调用返回false则剩余的beforeAction()不再调用Action 执行被取消默认情况下每次beforeAction()调用都会触发一个beforeAction事件你可以在事件上挂接处理器。在基类实现中该事件通过ActionEvent携带动作信息并以其isValid属性决定是否继续见 framework/base/Controller.php。运行 Action控制器执行 Action。此时 Action 参数会从请求数据中被解析并填充即上文bindActionParams()的绑定过程。afterAction 阶段控制器依次调用控制器自身、模块若属于模块、应用的afterAction()方法默认情况下每次afterAction()调用都会触发一个afterAction事件。基类实现中该事件的result属性会被作为最终返回值传递见 framework/base/Controller.php。生成响应应用接收 Action 的执行结果并将其赋值给响应组件。上述编排逻辑的源码实现在Controller::runAction()中见 framework/base/Controller.php它先解析 Action再沿模块链 → 控制器方向调用beforeAction()成功则执行$action-runWithParams($params)随后按控制器 → 模块链方向调用afterAction()最终返回结果给应用。值得补充的是Web 控制器的beforeAction()还承担了CSRF 校验职责当enableCsrfValidation为true且请求的 CSRF Token 校验失败时会抛出BadRequestHttpException见 framework/web/Controller.php。这解释了前文 controllerMap 示例中为何可以通过配置数组关闭某个控制器的 CSRF 校验。最佳实践Best Practices在良好的应用设计中控制器往往非常轻薄thin每个 Action 只包含寥寥几行代码。如果你的控制器变得相当复杂通常意味着你应该重构把部分代码迁移到其他类中。以下是几条具体的控制器实践建议。控制器可以访问请求数据可以用请求数据调用模型及其他服务组件的方法可以使用视图来组装响应不应处理请求数据——这应该由模型层完成不应内嵌 HTML 或其他展示性代码——这更适合放在视图中。遵循这些原则可以让控制器保持职责单一、易于测试也让模型与视图各司其职整个应用的维护成本显著降低。相关阅读应用结构总览了解控制器在整个应用骨架中的位置模型 与 视图控制器的两大协作对象路由与 URL 生成路由如何被解析为 Action模块在模块中使用控制器请求与响应 / 响应Action 参数来源与返回值去向配置controllerMap 与 defaultRoute 的配置语义核心源码framework/base/Controller.php、framework/web/Controller.php、framework/base/Module.php、framework/base/Action.php赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 控制器Controller完全指南动作、路由、生命周期与最佳实践Yii 2 控制器Controller完全指南动作、路由、生命周期与最佳实践 控制器是 Yii 2 框架 MVC 架构的核心枢纽它接收请求reques后端Web框架Yii 2 控制器Controller完全指南路由、动作、生命周期与最佳实践Yii 2 控制器Controller完全指南路由、动作、生命周期与最佳实践 导读 控制器Controller是 Yii 2 中 MVC 架构的核心组后端Web框架Yii 2 控制器Controllers完全指南路由解析、动作机制与生命周期深度解析Yii 2 控制器Controllers完全指南路由解析、动作机制与生命周期深度解析 控制器Controller是 Yii 2 MVC 架构中的核心枢后端Web框架上一篇VoiceprintRecognition-Pytorch深度解析多模型声纹识别系统架构与生产级部署实践下一篇Respond.js性能监控如何量化老旧浏览器中的优化效果创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表