14 нояб. 2015 г., 17:13

(modx/htaccess/RewriteRule) - простейший редирект: test.html => resource0.html => index.php?q=resource0.html

Вопрос непосредственно modx не касается, но, возможно, в описанном поведении виноват именно он, возможно, нет. В файле .htaccess у всех прописаны следующие директивы:
RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
Что здесь происходит — всем понятно. Предположим, на уровне Apache необходимо выполнить внутренний редирект с URI test.html на resource0.html (пример сильно упрощён, чтобы можно было сосредоточиться на проблеме).
Решить задачу можно 2 способами: 1.Перед стандартным редиректом modx (index.php?q=$1) прописать директивы:
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^test\.html$ resource0.html [QSA]
Здесь флаг [L] мы не ставим, чтобы после нашего редиректа сработал стандартный редирект modx (index.php?q=$1)
Загружаем в браузере URL site.ru/test.html Получаем: внешний (301) редирект на URL site.ru/resource0.html
2.Перед стандартным редиректом modx (index.php?q=$1) прописываем общий редирект с test.html на index.php?q=resource0.html
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^test\.html$ index.php?q=resource0.html [L,QSA]
Здесь указываем флаг [L], хотя и не обязательно, т.к. стандартный редирект modx после этого редиректа так и так не сработает.
Загружаем в браузере URL site.ru/test.html Получаем: внешний (301) редирект на URL site.ru/resource0.html
Итог: В обоих случаях получаем редирект на нужную нам страницу (resource0.html), но редирект выполняется внешний, а не внутренний. Почему ?
P.S. Для воспроизведения описанной ситуации достаточно изменить site.ru на имя своего сайта, resource0.html — на URI любого существующего ресурса.
Если в корне сайта создать файл resource0.html (с содержимым, например, OK), а в .htaccess прописать те же самые директивы, но без участия modx (т.е. без вызова index.php): RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^test\.html$ resource0.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ resource0.html?q=$1 [L,QSA] то никаких артефактов наблюдаться не будет — будет выполнен внутренний редирект на файл resource0.html (в браузере отобразится текст «OK», при этом в строке браузера останется исходный URL site.ru/test.html).
Отсюда делаем 2 вывода: 1.Внешний (301) редирект выполняет не Apache, а modx (судя по всему, анализирует содержимое полей глобальной переменной $_SERVER — иначе как modx узнает, что на уровне веб-сервера был изменён URI ресурса, который ему втюхали) 2.Данный вопрос здесь — очень в тему ))
P.S. На 50% проблема решена. Дело за малым — исходники modx…
Если в корне сайта создать файл resource0.html (с содержимым, например, OK), а в .htaccess прописать те же самые директивы, но без участия modx (т.е. без вызова index.php):
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^test\.html$ resource0.html [QSA] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ resource0.html?q=$1 [L,QSA]
то никаких артефактов наблюдаться не будет — будет выполнен внутренний редирект на файл resource0.html (в браузере отобразится текст «OK», при этом в строке браузера останется исходный URL site.ru/test.html).
Отсюда делаем 2 вывода: 1. Внешний (301) редирект выполняет не Apache, а modx (судя по всему, анализирует содержимое полей глобальной переменной $_SERVER — иначе как modx узнает, что на уровне веб-сервера был изменён URI ресурса, который ему втюхали) 2. Данный вопрос здесь — очень в тему ))
P.S. На 50% проблема решена. Дело за малым — исходники modx…
1. Внешний (301) редирект выполняет не Apache, а modx (судя по всему, анализирует содержимое полей глобальной переменной $_SERVER — иначе как modx узнает, что на уровне веб-сервера был изменён URI ресурса, который ему втюхали)
Всё так и есть. При включенных дружественных URL и включенной опции (friendly_urls_strict) в методе (modRequest::_cleanResourceIdentifier()) выполняется внешний (301) редирект с внешнего URI (запрошенного со стороны браузера) на внутренний URI того ресурса, который должен отображаться:
/** * Cleans the resource identifier from the request params. * * @param string $identifier The raw identifier. * @return string|integer The cleansed identifier. */ public function _cleanResourceIdentifier($identifier) { if (empty ($identifier)) { ... } elseif ($this->modx->getOption('friendly_urls', null, false) && $this->modx->resourceMethod == 'alias') { $containerSuffix = trim($this->modx->getOption('container_suffix', null, '')); $found = $this->modx->findResource($identifier); if ($found === false && !empty ($containerSuffix)) { ... } elseif ((integer) $this->modx->getOption('site_start', null, 1) === $found) { ... } else { if ($this->modx->getOption('friendly_urls_strict', null, false)) { $requestUri = $_SERVER['REQUEST_URI']; $qsPos = strpos($requestUri, '?'); if ($qsPos !== false) $requestUri = substr($requestUri, 0, $qsPos); $fullId = $this->modx->getOption('base_url', null, MODX_BASE_URL) . $identifier; $requestUri = urldecode($requestUri); if ($fullId !== $requestUri && strpos($requestUri, $fullId) !== 0) { $parameters = $this->getParameters(); unset($parameters[$this->modx->getOption('request_param_alias')]); $url = $this->modx->makeUrl($found, $this->modx->context->get('key'), $parameters, 'full'); $this->modx->sendRedirect($url, array('responseCode' => 'HTTP/1.1 301 Moved Permanently')); } } ... } } else { ... } return $identifier; }
Это штатные действия modx, которые реализуют логику «строгих» (канонических) URL при включении опции (friendly_urls_strict):
Если выбрано «Да», неканонические запросы, соответствующие ресурсу, будут перенаправлены с кодом 301 на канонический URI для этого ресурса. WARNING: Do not enable if you use custom rewrite rules which do not match at least the beginning of the canonical URI. For example, a canonical URI of foo/ with custom rewrites for foo/bar.html would work, but attempts to rewrite bar/foo.html as foo/ would force a redirect to foo/ with this option enabled.
Поэтому, если одновременно необходимо: а) исключить URI-дубли страниц включением опции (friendly_urls_strict) (необходимо для SEO) б) на уровне веб-сервера выполнять внутренний редирект с одного ресурса на другой
то нужно будет выполнить следующие действия: 1. Включить опцию (friendly_urls_strict) 2. Прописать требуемые директивы для редиректа в настройках веб-сервера (или в .htaccess)
3. В плагине на событии (OnHandleRequest) (последнее событие, которое генерируется до вызова метода modRequest::_cleanResourceIdentifier()) установить эту опцию в значение (false) для тех исходных URL, для которых выполняется внутренний редирект на уровне веб-сервера (выявить такие ситуации можно сравнением значения глобальной переменной $_SERVER['REQUEST_URI'] и значением переменной запроса q ($modx->getOption('request_param_alias')) — именно так и делает метод modRequest::_cleanResourceIdentifier())
4. (НЕ ОБЯЗАТЕЛЬНО) В плагине на событии (OnWebPageInit) (первое событие, которое генерируется после вызова метода modRequest::_cleanResourceIdentifier()) установить эту опцию в исходное значение (исходное значение при этом нужно запомнить в предыдущем плагине).
P.S. При таком подходе в тех случаях, когда выполняется внутренний редирект на уровне веб-сервера, контролировать уникальность внешних URL придётся самому (поскольку для этих случаев описанными выше действиями мы отключаем такой контроль со стороны modx).

Добавить комментарий