# 路由 _路由(Routing)_ 是 Pagekit 解决的一个中心问题。当浏览器点击一个 URL,框架会确认哪些操作会被调用。 [toc=2] ## 控制器 在 Pagekit 中创建路由的最常见方式是定义一个 _控制器(controller)_。控制器可以在处理请求、设置路由和渲染视图时发出响应。 ### 注册控制器 可以在[模块配置](224131)中注册控制器。使用 `routes` 属性将控制器添加到路由上。 ```php 'routes' => [ '/hello' => [ 'name' => '@hello/admin', 'controller' => [ 'Pagekit\\Hello\\Controller\\HelloController' ] ] ], ``` ### 基本结构 以 `@Route("/hello")` 注释的类,使得控制器被_添加_到 `http://example.com/hello/`。意味着它会响应这个 URL 及其子级 URL的所有请求,例如 `http://example.com/hello/settings`. ```php namespace Pagekit\Hello\Controller; /** * @Route("/hello") */ class HelloController { public function indexAction() { // ... } public function settingsAction() { // ... } } ``` 默认地,你的扩展(或主题)会被启动,并且一系列默认路由会被自动生成。可以使用[开发者工具栏](224147) 浏览刚才注册的路由(以及所有核心路由)。 这里说明如何理解路由: |路由 | 例子 | 描述| |------|------------------|---------------------------------------------------| |Name |`@hello/hello/settings`| 路由的名称可用于生成 URL(必须是唯一的)| |URI |`/hello/settings`| 在浏览器中访问此路由的路径| |Action|`Pagekit\Hello\Controller\DefaultController::settingsAction`| 即将被调用的控制器操作| 默认地,路由会是 `http://example.com/<extension>/<controller>/<action>` 这样的形式。特殊操作 `indexAction`,不会附加到 `.../index` 上,而是附加到 `.../`。自定义路由的高级选项当然也是可用的,在下文中会看到。 **Note** 在你的应用程序中,如果一个路由不是唯一的,真正被使用的是首先添加的那一个。由于这是框架内部的行为,说不定什么时候就会改变,所以不应该依赖它,要确保你的路由是唯一的。 ## 注释/Annotations 许多的控制器行为是由注释信息来确定类和方法的。下表是关于各种注释的描述: |注释 | 描述| |---------- | -------------------------------------------------------------| |`@Route` | 被附加操作或整个控制器的路由| |`@Request` | 处理从 http 请求传递到方法的参数| |`@Access` | 检查用户权限| **Note** 只有以两个星号开头的多行注释会生效,一个星号的不会。 ``` // will NOT work: /* @Route("/hello") */ // will work: /** @Route("/hello") */ // will work: /** * @Route("/hello") */ ``` ### @Route 定义即将被附加控制器(或控制器操作)的路由。它可以注释到类和方法。 默认地,名为 `greetAction` 的方法会在类的路由下,会作为 `/greet` 的形式来附加。要添加自定义路由,可以为方法添加任意数目的附加路由。路由还可以包含即将传递到方法的动态参数。 ```php /** * @Route("/greet", name="@hello/greet/world") * @Route("/greet/{name}", name="@hello/greet/name") */ public function greetAction($name = 'World') { // ... } ``` 可以指定参数来满足特定的需求(例如,将值限制为数字)。你可以命名一个路线,这样你可以从代码参考。在方法定义中,使用PHP参数的默认值,使参数可选。 路由可用绑定特定的 HTTP 方法, (e.g. `GET` or `POST`)。这对 RESTfil API 特别有用。 ```php /** * @Route("/view/{id}", name="@hello/view/id", requirements={"id"="\d+"}, methods="GET") */ public function viewAction($id = 0) { // ... } ``` **Note** 了解更多详情和例子,查看[Symfony 文档](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html) 的 `@Route` 注释。 ### @Request 可以指定通过请求传递的数据类型,并将其匹配到要传递到目标方法的参数上。 数组将 _name_ 映射到 _type_。 _name_ 请求数据中的键。_type_ 可以是 `int`, `string`, `array` 或者其他高级类型比如整数数组的 `int[]`。如果没有指定类型,则默认假设是 `string` 。 键的顺序将决定传递到方法的参数的顺序。方法头部的参数名称可以是任意形式的。 ```php /** * @Request({"id": "int", "title", "config": "array"}, csrf=true) */ public function saveAction($id, $title, $config) { // ... } ``` 还可以检查 token 来防范 [CSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)。添加 `csrf=true` 到请求方法的注释里,并在要提交表单到这个方法的视图中添加 `@token` 调用。 查看 `Pagekit\Filter\FilterManager` 中包含所有可用过滤器的列表。某些过滤器有附加的选项,比如 `pregreplace`。 ```php /** * @Request({"folders":"pregreplace[]"}, options={"folders" = {"pattern":"/[^a-z0-9_-]/i"}}) */ public function deleteFolders($folders) { // ... } ``` ### @Access 可以指定访问特定方法或整个控制器的用户权限。 控制器应该具体到前端页面或管理面板。到目前为止,我们看到了前端页面的控制器。管理控制器只能被拥有 `Access admin area` 权限的用户访问。还有,该控制器的所有路由的 URL 都以 `admin/` 开头。最后,会在管理界面视图中渲染,而不是默认的主题中。 ```php /** * @Access(admin=true) */ class SettingsController { // ... } ``` 现在,只有拥有 `Access admin area` 权限的用户可以访问此控制器的操作。如果想要进一步限制只允许某些用户进行特定的操作(比如管理用户等),你可以为单个控制器操作添加限制条件。 在`extension.php` (或 `theme.php`)文件中定义权限,并以你希望的方式组合它们。控制器级的访问限制会与单个操作上的访问限制组合。因此你可以为控制器设置一个基础的_最小_访问等级并限制某些操作,比如行政管理操作,则需要更多具体权限。 ```php /** * @Access("hello: manage users") */ public function saveAction() { // ... } ``` 当然,即使控制器并非管理区域的控制器,你还是可以使用这些限制。还可以在单个控制器操作上检查管理权限。 ```php /** * @Access("hello: edit article", admin=true) */ public function editAction() { // ... } ``` ## 生成 URL 使用URL 服务,为路由生成 URL。 ```php $this['url']->route('@hello/default/index') // '/hello/default/index' $this['url']->route('@hello/default/index', true) // 'http://example.com/hello/default/index' $this['url']->route('@hello/view/id', ['id' => 23]) // '/hello/view/23' ``` ## 链接 Pagekit 的路由可以用内部的路由语法描述。链接由路由的名称(e.g. '@hello/name')和可选的 GET 参数(e.g. '@hello/name?name=World')组成。它将实际的路由 URI 与路由本身分离。