понедельник, 6 июля 2015 г.

Проблема с catch-all и rewriteMaps

Задача: используя IIS URL Rewrite, перенаправить всё содержимое старого каталога (clients) в новое место (customers). Например, /clients/Gazprom/ в /customers/.

Это просто:

  <rule name="Сatch-all">
    <match url="^clients/" />
    <action type="Redirect" url="customers/" />
  </rule>

Поскольку в url я не использовал знак $ (конец строки), то отлавливаются все строки, начинающиеся на clients/.

А что, если у нас таких каталогов несколько (clients / partnerts / advertisers)? Пожалуйста: <match url="^(clients|partners|advertisers)/" />

А если их десяток? Причем нужно перенаправлять в разные места.
Тоже несложно - воспользуемся "картами":

<rewriteMaps>
<rewriteMap name="CatchAllRedirects">
  <add key="old1" value="new1" />
  <add key="old2" value="new2" />
  <add key="old3" value="new3" />
  <add key="old4" value="new4" />
</rewriteMap>
</rewriteMaps>

....

  <rule name="Catch-all">
    <match url="^([^/]+)/" />
    <conditions>
      <add input="{CatchAllRedirects:{R:1}}" pattern="(.+)"/>
    </conditions>
    <action type="Redirect" url="{C:1}/" />
  </rule>

Здесь мы перенаправляем /old1/*/ в /news1/ , /old2/*/ в /news1/ и т.д.


Пока что ничего особенного. Сложности возникают, если в старой URL может быть несколько уровней вложенности. Т.е. нужно перепроверить:

/old1/*/
/old1/*/*/
/old1/*/*/*/
/old2/old3/*/
/old2/old3/*/*/*/

Казалось бы, просто меняем ([^/]+) (хоть один символ, но не слэш) на (.+) (хоть один символ, любой):


  <rule name="Catch-all">
    <match url="^(.+)/" />
    <conditions>
      <add input="{CatchAllRedirects:{R:1}}" pattern="(.+)"/>
    </conditions>
    <action type="Redirect" url="{C:1}/" />
  </rule>


И добавляем в карту вложенные каталоги:

<rewriteMap name="CatchAllRedirects">
  <add key="old1" value="new1" />
  <add key="old2/old3" value="new2" />
   ....
</rewriteMap>

И не работает.

Потому что {R:1} (результат регулярного выражения, который используется как ключ для поиска) всегда один и только один. Правило не станет перебирать все возможные варианты (с одним слэшем, двумя, тремя) и пытаться найти соответвующий ключ в карте. Проблема в том, что при использовании карты обработка правила происходит в два шага: сначала регулярное выражение в <match>, и только потом поиск значения в карте.

Если же всё старые каталоги запихнуть в регулярное выражение и не использовать карту, то проблемы нет. Или можно сделать отдельные правила для разных уровней вложенности старого каталога. Но, конечно, если таких уровней десяток, то это непрактично...

Комментариев нет:

Ratings by outbrain