cyrax_02 14 ноября 2015 0 3
Вопрос непосредственно 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 любого существующего ресурса.
3 комментария
c
cyrax_02 14 ноября 2015г в 23:29 #
Если в корне сайта создать файл 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…
c
cyrax_02 14 ноября 2015г в 23:31 #
Если в корне сайта создать файл 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…
c
cyrax_02 15 ноября 2015г в 19:30 #
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).
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.