среда, 24 декабря 2008 г.

Библиотека urlrewrite

Недавно столкнулся с очень приятной библиотекой http://tuckey.org/urlrewrite/. Она представляет собой обычный фильтр который позволяет задавать свои настройки в хмл файле. Прелесть этой вещицы во первых в простоте (от первого знакомства до использования не более 5 минут), а во вторых в функционале (сейчас я с трудом себе могу представить как мои веб приложения обходились без нее).

Главная фича даной библиотеки: вы можете переформатировать запрос пользователя до не узнаваемости и затем перенаправить на нужный вам адрес. Это полезно во первых если вы хотите предоставить структурированые каким либо образом url-ы пользователю, во вторых, если вам просто нужно средиректить пользователя на другой адрес который имеет специфический формат и в третьих если ваши картинки, стили и ява скрипты запрашиваются клиентом по одному адресу, а вам по каким-либо причинам их удобно держать в другом.

Инсталяция: на сайте проэкта все детально описано, состоит из 3-х шагов: бросьте в папку WEB-INF/lib саму библиотеку urlrewrite.jar, в папку WEB-INF файл urlrewrite.xml (в котором вы будете описывать правила работы фильтра), и добавьте магические строки в web.xml:
1 <filter>
2 <filter-name>UrlRewriteFilter</filter-name>
3 <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
4 <init-param>
5 <param-name>logLevel</param-name>
6 <param-value>DEBUG</param-value>
7 </init-param>
8 <init-param>
9 <param-name>confReloadCheckInterval</param-name>
10 <param-value>5</param-value>
11 </init-param>
12 </filter>
13 <filter-mapping>
14 <filter-name>UrlRewriteFilter</filter-name>
15 <url-pattern>/*</url-pattern>
16 </filter-mapping>
в строке 6 указывается необязательный параметр - уровень вывода логов, а в строке 10 - интервал перечитывания файла urlrewrite.xml (даная конфигурация позволяет изменять его на лету, очень полезно на этапе разработки).

Конфигурирование файла urlrewrite.xml:
  1. может содержать несколько тегов rule, при этом они будут выполнятся один за другим сверху в низ
  2. для задания адресов используется Java Regular Expression. Кратко о синтаксисе можно прочитать например тут http://htmlweb.ru/java/regexp.php
  3. тег from - задает адреса на которые будет срабатывать фильтр
  4. тег to - указывает адрес куда будет перенаправлен пользователь (если используете redirect может пригодится внутренняя переменная %{context-path} - содержащая контекст вашего приложения).
    Пример:
    Если ваше приложение находится по адресу localhost/myapp то
    1 <to type="redirect">%{context-path}/index.jsp</to>
    пренаправит пользователя по адресу localhost/myapp/index.jsp
  5. condition - позволяет добавить дополнительные параметры, для того чтоб ограничить запросы на которые будет срабатывать фильтр.
    Пример:
    1 <condition type="request-uri" operator="notequal">/manager</condition>
    указывает что фильтр не должен срабатывать на запросы в которых встречается комбинация "/manager"
  6. set - позволяет записать информацыю в сессию, запрос, куки и т.д.
    Пример:
    1 <set name="publisherDomain" type="session">myDomain</set>
    записывает строку "myDomain" в переменную publisherDomain.
Маленький экскурс в регулярные выражения:
  • ^ - начало строки (пример: ^hi - значит что строка должна начинатся фразой "hi"
  • $ - конец строки

  • . - любой символ
  • [abc] - любой символ из заключенных в квадратные скобки
  • [^abc] - любой символ кроме заключенных в квадратные скобки
    можно также указать диапазон или тип символов

  • * - повторение символа 0 или более раз. (пример: ^/.* - строка начинается с / за которым следует любая комбинация символов)
  • + - как и * но обязательное повторение 1 раз или более
  • ? - повторение 0 или 1 раз

  • () -запомнить найденное выражение
  • $1, $2, ... - использовать запомненное выражение
    Пример:
    1 <rule>
    2 <from>^/(.+)$</from>
    3 <to type="redirect">/$1/login.html</to>
    4 </rule>
    Фильтр будет срабатывать на все адреса начинающиеся с "/" за которым следует хотя бы один символ и запоминать символы после "/". Т.е. если пользователь введет адрес /test, то фильтр перенаправит его на страницу /test/login.html
Для закрепления пример файла urlrewrite.xml:

1 <?xml version="1.0" encoding="utf-8"?>
2 <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN"
3
"http://tuckey.org/res/dtds/urlrewrite3.2.dtd">

4
5 <urlrewrite>
6
7 <rule>
8 <from>^/portal$</from>
9 <to type="redirect">%{context-path}/portal/index.jsp</to>
10 </rule>
11
12 <rule>
13 <condition type="request-uri" operator="notequal">/portal</condition>
14 <condition type="request-uri" operator="notequal">/css</condition>
15 <condition type="request-uri" operator="notequal">/js</condition>
16 <condition type="request-uri" operator="notequal">/img</condition>
17 <condition type="request-uri" operator="notequal">/html</condition>
18 <from>^.*/(.*)/(.*)$</from>
19 <set name="publisherDomain" type="session">$1</set>
20 <to type="redirect">%{context-path}/portal/$2</to>
21 </rule>
22
23 </urlrewrite>
здесь первое правило будет срабатывать на запросы типа /portal и перенаправлять их на страницу /portal/index.jsp
Второе правило будет срабатывать на все остальные урлы кроме обращений связаных с загруской стилей, стат. страниц, картинок и т.д., далее оно будет вырезать выражение находящееся между первым и последним знаком "/"? и помещать его в сесию, а пользователя перенаправлять по адресу /myapp/portal/выражение_стоящее_за_последним_знаком_/

8 комментариев:

Vladimir комментирует...

Просьба помочь прописать правило, чтобы страницы без / редиректило на страницу с / в конце. При этом нужно исключить запросы для .css, .js, url с параметрами и т.д.

Moritur комментирует...

1) Добавляем condition .css, .js и т.д. чтоб исключить работу правил для url с такими строками
2) пишем регулярное выражение ^/(.+[^/])$ Здесь: ^ - начало строки, $ - конец, .+ - любой один и более символ, [^/] - последний символ не /, () - для записи всего что в них в переменную $1 для дальнейшего использования

получаем

<rule>
<condition type="request-uri" operator="notequal">.css</condition>
<condition type="request-uri" operator="notequal">.js</condition>
<from>^/(.+[^/])$</from>
<to type="redirect">%{context-path}/$1/</to>
</rule>

Владимир комментирует...

Большое спасибо за ответ!

Так а чтобы правила работали для всех URL, в которых содержатся символы: . ? # и т.п., в исключениях достаточно описать:
.
?
#
&

Или нужно экранировать?

\<.\>
\
\<#\>
\<&\>

Moritur комментирует...

Экранировать нужно управляющие символы в регулярных выражениях используя java строки. То есть в премере выше ".css" - это регулярное выражение и если вдруг вам понадобится там ? то писать нужно "\\?".
но для вашей конкретной задачи вам проще добавить условие:
<condition type="query-string" operator="notequal">.+</condition>
-обратите внимание, 'то условие говорит что наше правило не работает для запросов у которых кверистриг в длину больше 1-цы

Анонимный комментирует...

Спасибо за полезные пояснения по UrlRewriteFilter
Пытаюсь подключить фильтр для тестового проекта и не получатеся.
Задача: Все запросы перенаправлять на один и тот-же сервлет.
Тоесть любой URI будь то /index.jsp или /any.jsp должен заставить работать один сервлет у котрого прописан один едиственный паттерн @WebServlet(name = "ServletClass", urlPatterns = {"/index.jsp"})
Данному сервлету нужно будет передать реальный запрошенный URI а не тот который он получит из своего request.
Подскажите пожалуйста как решить задачку использования одного сервлета для разных URI с помощью UrlRewriteFilter

Moritur комментирует...
Этот комментарий был удален автором.
Moritur комментирует...

Привет, вам помогут функции и переменные, смотрите детальнее вот тут: http://cdn.rawgit.com/paultuckey/urlrewritefilter/master/src/doc/manual/4.0/index.html#configuration

можно сделать вот так:

<rule>
<condition type="request-uri" operator="notequal">index.jsp</condition>
<from>^/(.*)$</from>
<to type="redirect">/test/index.jsp?path=${escape:utf8:%{request-url}?%{query-string}}</to>
</rule>

Где test - имя проекта, escape - URLEncoding, %{request-url}?%{query-string} - HttpServletRequest. getRequestURL() + '?' + HttpServletRequest. getQueryString()

Алексей комментирует...

Добрый день!

Не удаётся настроить редирект в следующих условиях:

urlrewrite.xml лежит тут:

webapps/ROOT/WEB-INF/urlrewrite.xml

В директории webapps есть раздел: form/ya/

В urlrewrite.xml задаю правило:

^/form/ya/*
/mypage


В итоге получается, что если я иду на mysite.ru/form/ya/test, то я попадаю на 404 tomcat, если же я в webapps изменяю название раздела form, например, на form_, то в этом случае правило отрабатывает и редиректит на /mypage.

Почему не срабатывает правило, если в директории webapps раздел существует, для которого настроено правило, а если его нет, то все ок?