mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-22 16:06:53 +01:00
415 lines
26 KiB
Plaintext
415 lines
26 KiB
Plaintext
Управление URL
|
||
==============
|
||
|
||
Управление URL-адресами в веб-приложениях включает в себя два аспекта:
|
||
|
||
1. Приложению необходимо разобрать запрос пользователя, поступающий в виде URL,
|
||
на отдельные параметры.
|
||
2. Приложение должно предоставлять способ формирования адресов URL, с которыми
|
||
оно сможет корректно работать.
|
||
|
||
В приложениях на Yii эти задачи решаются с использованием класса [CUrlManager].
|
||
|
||
> Note|Примечание: Вы можете не пользоваться Yii для генерации URL, однако,
|
||
так делать не рекомендуется, потому как вы не сможете легко поменять URL
|
||
приложения через конфигурацию без изменения кода.
|
||
|
||
Создание адресов URL
|
||
--------------------
|
||
|
||
В принципе, адреса URL можно задать прямо в коде представлений контроллера,
|
||
однако куда удобнее создавать их динамически:
|
||
|
||
~~~
|
||
[php]
|
||
$url=$this->createUrl($route,$params);
|
||
~~~
|
||
|
||
где `$this` относится к экземпляру контроллера; `$route` соответствует [маршруту](/doc/guide/basics.controller#route) запроса,
|
||
а `$params` является списком параметров `GET` для добавления к URL.
|
||
|
||
По умолчанию, адреса создаются посредством [createUrl|CController::createUrl] в `get`-формате.
|
||
Например, при значениях параметров `$route='post/read'` и `$params=array('id'=>100)`, получим такой URL:
|
||
|
||
~~~
|
||
/index.php?r=post/read&id=100
|
||
~~~
|
||
|
||
где параметры указаны в виде набора пар `имя=значение`, соединенных знаком `&`, а
|
||
параметр `r` указывает на [маршрут](/doc/guide/basics.controller#route). Однако, этот формат не очень
|
||
дружелюбен по отношению к пользователю.
|
||
|
||
> Tip|Подсказка: Для того, чтобы сгенерировать URL с хештегом, к примеру,
|
||
`/index.php?r=post/read&id=100#title`, необходимо передать параметр `#`
|
||
следующим образом: `$this->createUrl('post/read',array('id'=>100,'#'=>'title'))`.
|
||
|
||
Мы можем сделать так, чтобы адрес, приведенный в качестве примера выше, выглядел более аккуратно и понятно
|
||
за счет использования формата `path`, который исключает использование
|
||
строки запроса и включает все GET-параметры в информационную часть адреса URL:
|
||
|
||
~~~
|
||
/index.php/post/read/id/100
|
||
~~~
|
||
|
||
Для изменения формата представления адреса URL, нужно настроить компонент приложения [urlManager|CWebApplication::urlManager]
|
||
таким образом, чтобы метод [createUrl|CController::createUrl] мог автоматически переключиться на использование нового формата, а
|
||
приложение могло корректно воспринимать новый формат адресов URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
…
|
||
'components'=>array(
|
||
…
|
||
'urlManager'=>array(
|
||
'urlFormat'=>'path',
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
Обратите внимание, что указывать класс компонента [urlManager|CWebApplication::urlManager] не требуется, т.к.
|
||
он уже объявлен как [CUrlManager] в [CWebApplication].
|
||
|
||
> Tip|Подсказка: Адрес URL, генерируемый методом [createUrl|CController::createUrl] является относительным. Для того, чтобы получить
|
||
абсолютный адрес, нужно добавить префикс, используя `Yii::app()->request->hostInfo`, или вызвать метод
|
||
[createAbsoluteUrl|CController::createAbsoluteUrl].
|
||
|
||
Человекопонятные URL
|
||
--------------------
|
||
|
||
Если в качестве формата адреса URL используется `path`, то мы можем определить правила формирования URL, чтобы
|
||
сделать адреса более привлекательными и понятными с точки зрения пользователя. Например, мы можем использовать
|
||
короткий адрес `/post/100` вместо длинного варианта `/index.php/post/read/id/100`. [CUrlManager] использует правила формирования URL
|
||
как для создания, так и для обработки адресов.
|
||
|
||
Правила формирования URL задаются путем конфигурации свойства [rules|CUrlManager::rules] компонента приложения
|
||
[urlManager|CWebApplication::urlManager]:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
…
|
||
'components'=>array(
|
||
…
|
||
'urlManager'=>array(
|
||
'urlFormat'=>'path',
|
||
'rules'=>array(
|
||
'pattern1'=>'route1',
|
||
'pattern2'=>'route2',
|
||
'pattern3'=>'route3',
|
||
),
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
Правила задаются в виде массива пар шаблон-путь, где каждая пара соответствует одному правилу.
|
||
Шаблон правила — строка, которая должна совпадать с путём в URL. Путь правила должен
|
||
указывать на существующий [путь контроллера](/doc/guide/basics.controller#route).
|
||
|
||
Кроме показанного выше способа задания правил, можно описать правило с указанием
|
||
дополнительных параметров:
|
||
|
||
~~~
|
||
[php]
|
||
'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
|
||
~~~
|
||
|
||
Начиная с версии 1.1.7, можно использовать показанный ниже формат. То есть
|
||
паттерн указывается как элемент массива, что позволяет указать несколько правил
|
||
одного паттерна:
|
||
|
||
~~~
|
||
[php]
|
||
array('route1', 'pattern'=>'pattern1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
|
||
~~~
|
||
|
||
Здесь массив содержит список дополнительных параметров для правила. Возможно
|
||
указать следующие параметры:
|
||
|
||
- [pattern|CUrlRule::pattern]: паттерн, который будет использован при сопоставлении
|
||
и создании URL. Данная возможность доступна с версии 1.1.7.
|
||
|
||
- [urlSuffix|CUrlRule::urlSuffix]: суффикс URL, используемый исключительно для данного правила.
|
||
По умолчанию равен null, что означает использование значения [CUrlManager::urlSuffix].
|
||
|
||
- [caseSensitive|CUrlRule::caseSensitive]: учитывает ли правило регистр. По умолчанию параметр
|
||
равен null, что означает использование значения [CUrlManager::caseSensitive].
|
||
|
||
- [defaultParams|CUrlRule::defaultParams]: GET-параметры по умолчанию (`имя=>значение`) для данного правила.
|
||
При срабатывании правила параметры будут добавлены в `$_GET`.
|
||
|
||
- [matchValue|CUrlRule::matchValue]: должны ли значения GET-параметров при создании URL совпадать
|
||
с соответствующими подвыражениями в основном правиле. По умолчанию параметр равен null,
|
||
что означает использование значения [CUrlManager::matchValue]. При значении параметра false
|
||
правило будет использовано для создания URL только если имена параметров совпадают с именами в правиле.
|
||
При значении true значения параметров дополнительно должны совпадать с подвыражениями в правиле.
|
||
Стоит отметить, что установка значения в true снижает производительность.
|
||
|
||
- [verb|CUrlRule::verb]: тип HTTP запроса (например, `GET`, `POST`, `DELETE`),
|
||
для которого работает данное правило. По умолчанию равен null, что означает
|
||
работу правила с любыми HTTP запросами. Если необходимо указать несколько
|
||
типов запросов, их надо разделить запятыми. В том случае, когда правило не
|
||
совпадает с текущим типом запроса, оно пропускается на этапе разбора запроса.
|
||
Данная опция используется только для разбора запроса и введена для поддержки
|
||
URL в стиле REST. Данная возможность доступна с версии 1.1.7.
|
||
|
||
- [parsingOnly|CUrlRule::parsingOnly]: использовать ли правило только на
|
||
этапе разбора запроса. По умолчанию параметр равен false, что означает, что
|
||
правило используется как для разбора запроса, так и для построения URL.
|
||
Данная возможность доступна с версии 1.1.7.
|
||
|
||
|
||
Использование именованных параметров
|
||
------------------------------------
|
||
|
||
Правило может быть ассоциировано с несколькими GET-параметрами. Эти параметры
|
||
указываются в шаблоне правила в виде маркеров следующим образом:
|
||
|
||
~~~
|
||
<ParamName:ParamPattern>
|
||
~~~
|
||
|
||
где `ParamName` соответствует имени GET-параметра, а необязательный
|
||
`ParamPattern` — регулярному выражению, которое используется для проверки
|
||
соответствия значению GET-параметра. Если `ParamPattern` не указан, то
|
||
параметр должен соответствовать любым символам, кроме слэша `/`. В момент
|
||
создания URL маркеры будут заменены на соответствующие значения параметров,
|
||
а в момент обработки URL, соответствующим GET-параметрам будут
|
||
присвоены результаты обработки.
|
||
|
||
Для наглядности приведем несколько примеров. Предположим, что наш набор правил состоит из трех правил:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'posts'=>'post/list',
|
||
'post/<id:\d+>'=>'post/read',
|
||
'post/<year:\d{4}>/<title>'=>'post/read',
|
||
)
|
||
~~~
|
||
|
||
- Вызов `$this->createUrl('post/list')` сгенерирует `/index.php/posts`. Здесь было применено первое правило.
|
||
|
||
- Вызов `$this->createUrl('post/read',array('id'=>100))` сгенерирует `/index.php/post/100`. Применено второе правило.
|
||
|
||
- Вызов `$this->createUrl('post/read',array('year'=>2008,'title'=>'a
|
||
sample post'))` сгенерирует `/index.php/post/2008/a%20sample%20post`.
|
||
Использовано третье правило.
|
||
|
||
- Вызов `$this->createUrl('post/read')` сгенерирует
|
||
`/index.php/post/read`. Ни одно из правил не было применено.
|
||
|
||
При использовании [createUrl|CController::createUrl] для генерации адреса URL, маршрут и GET-параметры, переданные
|
||
методу, используются для определения правила, которое нужно применить. Правило применяется в том случае, когда все параметры, ассоциированные с правилом,
|
||
присутствуют среди GET-параметров, а маршрут соответствует параметру маршрута.
|
||
|
||
Если же количество GET-параметров больше, чем требует правило, то лишние параметры
|
||
будут включены в строку запроса. Например, если вызвать `$this->createUrl('post/read',array('id'=>100,'year'=>2008))`,
|
||
мы получим `/index.php/post/100?year=2008`. Для того, чтобы лишние параметры были отражены в информационной части пути, необходимо
|
||
добавить к правилу `/*`. Таким образом, используя правило `post/<id:\d+>/*` получим URL вида `/index.php/post/100/year/2008`.
|
||
|
||
Как уже говорилось, вторая задача правил URL — разбирать URL-запросы. Этот процесс обратный процессу создания URL.
|
||
Например, когда пользователь запрашивает `/index.php/post/100`, применяется второе правило из примера выше и запрос
|
||
преобразовывается в маршрут `post/read` и GET-параметр `array('id'=>100)` (доступный через `$_GET`).
|
||
|
||
|
||
> Note|Примечание: Использование правил URL снижает производительность приложения.
|
||
Это происходит по той причине, что в процессе парсинга запрошенного URL
|
||
[CUrlManager] пытается найти соответствие каждому правилу до тех пор, пока
|
||
какое-нибудь из правил не будет применено. Чем больше правил, тем больший урон
|
||
производительности. Поэтому в случае высоконагруженных приложений использование
|
||
правил URL стоит минимизировать.
|
||
|
||
|
||
Параметризация маршрутов
|
||
------------------------
|
||
|
||
Мы можем использовать именованные параметры
|
||
в маршруте правила. Такое правило может быть применено к нескольким маршрутам,
|
||
совпадающим с правилом. Это может помочь уменьшить число правил и, таким образом,
|
||
повысить производительность приложения.
|
||
|
||
Для того, чтобы показать параметризацию маршрутов, используем следующий набор правил:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
|
||
'<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
|
||
'<_c:(post|comment)>s' => '<_c>/list',
|
||
)
|
||
~~~
|
||
|
||
Мы использовали два именованных параметра в маршруте правил: `_c` и `_a`.
|
||
Первый соответствует названию контроллера и может быть равен `post` или `comment`,
|
||
второй — названию action-а и может принимать значения `create`, `update` или `delete`.
|
||
Вы можете называть параметры по-другому, если их имена не конфликтуют с GET-параметрами,
|
||
которые могут использоваться в URL.
|
||
|
||
При использовании правил, приведённых выше, URL `/index.php/post/123/create`
|
||
будет обработано как маршрут `post/create` с GET-параметром `id=123`.
|
||
По маршруту `comment/list` с GET-параметром `page=2`, мы можем создать URL
|
||
`/index.php/comments?page=2`.
|
||
|
||
|
||
Параметризация имён хостов
|
||
--------------------------
|
||
|
||
Также возможно использовать имена хостов в правилах для разбора и
|
||
создания URL. Можно выделять часть имени хоста в GET-параметр.
|
||
Например, URL `http://admin.example.com/en/profile` может быть разобран в
|
||
GET-параметры `user=admin` и `lang=en`. С другой стороны, правила с именами
|
||
хостов могут также использоваться для создания URL адресов.
|
||
|
||
Чтобы использовать параметризованные имена хостов, включите имя хоста в правила URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
|
||
)
|
||
~~~
|
||
|
||
Пример выше говорит, что первый сегмент имени хоста должен стать
|
||
параметром `user`, а первый сегмент пути — параметром `lang`. Правило
|
||
соответствует маршруту `user/profile`.
|
||
|
||
Помните, что [CUrlManager::showScriptName] не работает при создании URL
|
||
адреса с использованием правил с параметризованным именем хоста.
|
||
|
||
Стоит отметить, что правило с параметризованным именем хоста не должно
|
||
содержать поддиректорий в том случае, если приложение находится в поддиректории
|
||
корня вебсервера. К примеру, если приложение располагается по адресу
|
||
`http://www.example.com/sandbox/blog`, мы должны использовать точно такое же
|
||
правило URL, как описано выше. Без поддиректории: `sandbox/blog`.
|
||
|
||
Скрываем `index.php`
|
||
--------------------
|
||
|
||
С целью сделать адрес URL еще более привлекательным можно спрятать имя входного скрипта `index.php`. Для этого необходимо настроить
|
||
веб-сервер и компонент приложения [urlManager|CWebApplication::urlManager].
|
||
|
||
Вначале сконфигурируем веб-сервер таким образом, чтобы адрес URL без указания имени входного скрипта по-прежнему
|
||
передавался на обработку входному скрипту. Для сервера [Apache HTTP server](http://httpd.apache.org/) это достигается
|
||
путем включения механизма преобразования URL и заданием нескольких правил. Для этого
|
||
необходимо создать файл `/wwwroot/blog/.htaccess`, содержащий правила, приведённые ниже.
|
||
Те же правила могут быть размещены в файле конфигурации Apache
|
||
в секции `Directory` для `/wwwroot/blog`.
|
||
|
||
~~~
|
||
RewriteEngine on
|
||
|
||
# if a directory or a file exists, use it directly
|
||
RewriteCond %{REQUEST_FILENAME} !-f
|
||
RewriteCond %{REQUEST_FILENAME} !-d
|
||
|
||
# otherwise forward it to index.php
|
||
RewriteRule . index.php
|
||
~~~
|
||
|
||
Далее нужно установить свойство [showScriptName|CUrlManager::showScriptName] компонента
|
||
[urlManager|CWebApplication::urlManager] равным `false`.
|
||
|
||
Теперь, вызвав `$this->createUrl('post/read',array('id'=>100))`, мы получим URL `/post/100`. Что важно, этот
|
||
адрес URL будет корректно распознан нашим веб-приложением.
|
||
|
||
Подмена окончания в адресе URL
|
||
------------------------------
|
||
|
||
В дополнение ко всему перечисленному выше, мы можем добавить к нашим адресам URL окончание. Например,
|
||
мы можем получить `/post/100.html` вместо `/post/100`, представив пользователю как будто бы статичную страничку.
|
||
Для этого нужно просто настроить компонент [urlManager|CWebApplication::urlManager] путем назначения свойству
|
||
[urlSuffix|CUrlManager::urlSuffix] любого желаемого окончания.
|
||
|
||
Использование своего класса правила URL
|
||
---------------------------------------
|
||
|
||
> Note|Примечание: данная возможность доступна с версии 1.1.8.
|
||
|
||
По умолчанию каждое правило URL для [CUrlManager] представлено объектом
|
||
класса [CUrlRule]. Этот объект разбирает запросы и создаёт URL по заданному правилу.
|
||
Несмотря на то, что [CUrlRule] достаточно гибок и подходит для работы с большинством
|
||
форматов URL, иногда требуются какие-либо особенные возможности.
|
||
|
||
К примеру, для сайта по продаже автомобилей может потребоваться поддерживать URL
|
||
вида `/Производитель/Модель`, где и `Производитель` и `Модель` должны соответствовать
|
||
данным из определённой таблицы базы данных. В этом случае класс [CUrlRule] не
|
||
подойдёт так как он, в основном, работает с статически описанными регулярными
|
||
выражениями, а не с базой данных.
|
||
|
||
В данном случае можно реализовать новый класс правила URL, унаследовав [CBaseUrlRule],
|
||
и использовать его в одном или нескольких правилах. Для приведённого выше примера
|
||
с продажей автомобилей подойдут следующие правила URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
// стандартное правило для обработки '/' как 'site/index'
|
||
'' => 'site/index',
|
||
|
||
// стандартное правило для обработки '/login' как 'site/login' и т.д.
|
||
'<action:(login|logout|about)>' => 'site/<action>',
|
||
|
||
// своё правило для URL вида '/Производитель/Модель'
|
||
array(
|
||
'class' => 'application.components.CarUrlRule',
|
||
'connectionID' => 'db',
|
||
),
|
||
|
||
// стандартное правило для обработки 'post/update' и др.
|
||
'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
|
||
),
|
||
~~~
|
||
|
||
Выше мы использовали свой класс правила URL `CarUrlRule` для обработки URL
|
||
вида `/Производитель/Модель`. Данный класс может быть реализован следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
class CarUrlRule extends CBaseUrlRule
|
||
{
|
||
public $connectionID = 'db';
|
||
|
||
public function createUrl($manager,$route,$params,$ampersand)
|
||
{
|
||
if ($route==='car/index')
|
||
{
|
||
if (isset($params['manufacturer'], $params['model']))
|
||
return $params['manufacturer'] . '/' . $params['model'];
|
||
else if (isset($params['manufacturer']))
|
||
return $params['manufacturer'];
|
||
}
|
||
return false; // не применяем данное правило
|
||
}
|
||
|
||
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
|
||
{
|
||
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
|
||
{
|
||
// Проверяем $matches[1] и $matches[3] на предмет
|
||
// соответствия производителю и модели в БД.
|
||
// Если соответствуют, выставляем $_GET['manufacturer'] и/или $_GET['model']
|
||
// и возвращаем строку с маршрутом 'car/index'.
|
||
}
|
||
return false; // не применяем данное правило
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Свой класс правила URL должен реализовать два абстрактных метода, объявленных в
|
||
[CBaseUrlRule]:
|
||
|
||
* [createUrl()|CBaseUrlRule::createUrl()]
|
||
* [parseUrl()|CBaseUrlRule::parseUrl()]
|
||
|
||
Кроме показанного выше типичного использования, свой класс URL может пригодиться
|
||
и в других ситуациях. Например, можно реализовать класс правила, который будет
|
||
записывать в журнал разбираемые и создаваемые URL. Это может пригодиться на
|
||
этапе разработки. Также можно реализовать класс, который показывает особую страницу
|
||
ошибки 404 в том случае, когда все остальные правила для разбираемого URL не сработали.
|
||
Стоит отметить, что в этом случае правило с этим специальным классом должно
|
||
указываться последним в списке.
|
||
|
||
<div class="revision">$Id: topics.url.txt 3329 2011-06-28 08:31:35Z mdomba $</div> |