Есть какие-нибудь хитроумные способы обработки контекста в веб-приложении?

StackOverflow https://stackoverflow.com/questions/125359

Вопрос

В Java веб-приложения входят в состав to WARs.По умолчанию многие контейнеры сервлетов будут использовать имя WAR в качестве контекстного имени приложения.

Таким образом, myapp.war будет развернут в http://example.com/myapp.

Проблема в том, что веб-приложение считает, что его "root" - это, ну, "root" или просто "/", тогда как HTML будет считать, что корнем вашего приложения является "/ myapp".

В API сервлетов и JSP есть средства, помогающие управлять этим.Например, если в сервлете вы делаете:response.sendRedirect("/mypage.jsp"), контейнер добавит контекст и создаст URL-адрес: http://example.com/myapp/mypage.jsp".

Однако вы не можете сделать этого, скажем, с тегом IMG в HTML.Если вы это сделаете <img src="/myimage.gif" />, вы, скорее всего, получите 404, потому что то, что вы действительно хотели, было "/myapp/myimage.gif".

Многие фреймворки имеют теги JSP, которые также учитывают контекст, и существуют различные способы создания правильных URL-адресов в JSP (ни один из них не является особенно элегантным).

Это серьезная проблема для программистов - выбрать, когда использовать "Относительный URL приложения", а не абсолютный URL.

Наконец, существует проблема с кодом Javascript, который должен создавать URL-адреса "на лету", и встроенными URL-адресами в CSS (для фоновых изображений и тому подобного).

Мне любопытно, какие методы используют другие, чтобы смягчить эту проблему и обойти ее.Многие просто запускают и жестко кодируют его либо для root-сервера, либо для любого другого контекста, который они используют.Я уже знаю этот ответ, это не то, что я ищу.

Чем вы занимаетесь?

Это было полезно?

Решение

Вы можете использовать JSTL для создания URL-адресов.

Например, <c:url value="/images/header.jpg" /> будет иметь префикс корневого контекста.

С CSS это обычно не является проблемой для меня.

У меня есть корневая структура веб-сайта, подобная этой:

/css
/изображения

Затем в файле CSS вам просто нужно использовать относительные URL-адреса (../images/header.jpg), и ему не нужно знать корневой контекст.

Что касается JavaScript, то для меня работает включение некоторого общего JavaScript в заголовок страницы следующим образом:

<script type="text/javascript">
var CONTEXT_ROOT = '<%= request.getContextPath() %>';
</script>

Затем вы можете использовать корневой каталог контекста во всех своих скриптах (или вы можете определить функцию для построения путей - это может быть немного более гибко).

Очевидно, что все это зависит от того, используете ли вы JSP и JSTL, но я использую JSF с Facelets, и используемые методы аналогичны - единственное реальное отличие заключается в получении корневого каталога контекста другим способом.

Другие советы

Для HTML-страниц я просто устанавливаю HTML <base> бирка.Каждое относительное звено (т.е.не начиная со схемы или /) станет по отношению к нему.Нет никакого чистого способа схватить его немедленно с помощью HttpServletRequest, так что здесь нам нужна небольшая помощь JSTL.

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<c:set var="req" value="${pageContext.request}" />
<c:set var="url">${req.requestURL}</c:set>
<c:set var="uri">${req.requestURI}</c:set>

<!DOCTYPE html>
<html lang="en">
  <head>
    <base href="${fn:substring(url, 0, fn:length(url) - fn:length(uri))}${req.contextPath}/" />
    <link rel="stylesheet" href="css/default.css">
    <script src="js/default.js"></script>
  </head>
  <body>
    <img src="img/logo.png" />
    <a href="other.jsp">link</a>
  </body>
</html>

Это, в свою очередь, однако, имеет одно предостережение:якоря (the #identifier URL-адреса) также станут относительными к базовому пути.Если у вас есть какой-либо из них, вы хотели бы вместо этого привязать его к URL-адресу запроса (URI).Итак, меняйтесь, как

<a href="#identifier">jump</a>

Для

<a href="${uri}#identifier">jump</a>

В JS вы можете просто получить доступ к <base> элемент из DOM всякий раз, когда вы хотите преобразовать относительный URL-адрес в абсолютный URL.

var base = document.getElementsByTagName("base")[0].href;

Или, если вы используете jQuery

var base = $("base").attr("href");

В CSS URL-адреса изображений привязаны к URL-адресу самой таблицы стилей.Итак, просто поместите изображения в какую-нибудь папку относительно самой таблицы стилей.Например.

/css/style.css
/css/images/foo.png

и ссылайтесь на них следующим образом

background-image: url('images/foo.png');

Если вы предпочитаете поместить изображения в какую-нибудь папку на том же уровне, что и папка CSS

/css/style.css
/images/foo.png

затем используйте ../ чтобы перейти в общую родительскую папку

background-image: url('../images/foo.png');

Смотрите также:

Я согласен с опоздание.Я тоже обратил свое внимание на фильтры и нашел решение в лице проекта UrlRewriteFilter Фильтр UrlRewriteFilter.Простая конфигурация, подобная следующей:

<rule>
    <from>^.+/resources/(.*)$</from>
    <to>/resources/$1</to>
</rule>

помогает перенаправлять все запросы на */resources path в /resources pass (включая префикс контекстного пути).Так что я могу просто вложить все свои Изображения и CSS файлы в разделе Ресурсы папку и продолжайте использовать относительные URL-адреса в моих стилях для фоновых изображений и других случаев.

API сервлетов и JSP имеют средства, помогающие управлять этим.Для Например, если в сервлете вы делаете:response.sendRedirect("/mypage.jsp"), контейнер добавит контекст и создаст URL-адрес: http://example.com/myapp/mypage.jsp".

Ах, может быть, а может и нет - это зависит от вашего контейнера и спецификации сервлета!

От Сервлет 2.3:Раскрыты новые возможности:

И, наконец, после продолжительных дебатов группой экспертов Servlet API 2.3 раз и навсегда прояснил, что именно что происходит при вызове res.sendRedirect("/index.html") для сервлета, выполняющегося в некорневом контексте.Проблема в том, что Servlet API 2.2 требует, чтобы неполный путь, такой как "/index.html", был переведен контейнером сервлета в полный путь, но не говорит как обрабатываются контекстные пути.Если сервлет, выполняющий вызов, находится в контексте по пути "/contextpath", должен ли URI перенаправления переводиться относительно корневого каталога контейнера (http://server:port/index.html) или корень контекста (http://server:port/contextpath/index.html)?Для максимальной переносимости необходимо определить поведение;после длительных дебатов эксперты решили перевести относительно корневого каталога контейнера.Для тех, кто хочет использовать контекст относительно, вы можете добавить выходные данные getContextPath() к вашему URI.

Так что нет, с 2.3 ваши пути следующие нет автоматически преобразуется, чтобы включить контекстный путь.

Я использовал вспомогательные классы для создания img-тегов и т.д.Этот вспомогательный класс заботится о пути с префиксами с помощью ContextPath приложения.(Это работает, но мне это не очень нравится.Если у кого-нибудь есть альтернативы получше, пожалуйста, сообщите.)

Для путей в css-файлах и т.д.Я использую Сценарий сборки Ant это использует site.production.css для site.css в производственной среде и site.development.css в среде разработки.

В качестве альтернативы я иногда использую Ant-скрипт, который заменяет токен @@ токены с соответствующими данными для разных сред.В этом случае @ContextPath@ token будет заменен правильным контекстным путем.

Один из вариантов - использовать "плоскую" структуру приложения и относительные URL-адреса, когда это возможно.

Под "плоским" я подразумеваю, что в корневом каталоге вашего приложения нет подкаталогов, возможно, всего несколько каталогов для статического содержимого в виде "images /".Все ваши JSP, URL-адреса действий, сервлеты находятся непосредственно в корневом каталоге.

Это не полностью решит вашу проблему, но значительно упростит ее.

Вильмантас сказал здесь правильное слово:относительные URL-адреса.

Все, что вам нужно сделать в вашем IMG, это использовать

<img src="myimage.gif"/>

вместо того, чтобы

<img src="/myimage.gif"/>

и это будет зависеть от контекста приложения (поскольку браузер интерпретирует URL-адрес для перехода).

За исключением особых случаев, я бы не рекомендовал использовать абсолютные URL-адреса таким образом.Никогда.Абсолютные URL-адреса хороши для случаев, когда другое веб-приложение указывает на что-то в вашем веб-приложении.Внутренне - когда один ресурс указывает на второй ресурс в том же контексте - ресурс должен знать, где он находится, поэтому он должен быть в состоянии указать относительный путь ко второму ресурсу.

Конечно, вы будете писать модульные компоненты, которые не знают ресурс, который их включает.Например:

/myapp/user/email.jsp:
Email: <a href="../sendmail.jsp">${user.email}</a>

/myapp/browse/profile.jsp:
<jsp:include page="../user/email.jsp" />

/myapp/home.jsp:
<jsp:include page="../user/email.jsp" />

Итак, как это происходит email.jsp знать относительный путь sendmail.jsp?Очевидно, что связь оборвется в любом случае /myapp/browse/profile.jsp или он разобьется о /myapp/home.jsp .Ответ заключается в том, чтобы хранить все ваши URL-адреса в одном и том же плоском пространстве путей к файлу.То есть, каждый URL-адрес не должен содержать косых черт после /myapp/ .

Это довольно легко сделать, если у вас есть какое-то соответствие между URL-адресами и реальными файлами, которые генерируют контент.(например,в Spring используйте DispatcherServlet для сопоставления URL-адресов с файлами JSP или представлениями.)

Есть особые случаи.например ,если вы пишете браузерное приложение на Javascript, то становится сложнее поддерживать единый путь к файлу.В этом случае, или в других особых случаях, или просто если у вас есть личные предпочтения, использовать его на самом деле не имеет большого значения <%= request.getContextPath() %> чтобы создать абсолютный путь.

Вы можете использовать request.getContextPath() для создания абсолютных URL-адресов, которые не являются жестко запрограммированными для определенного контекста.Как указывалось в предыдущем ответе, для JavaScript вы просто устанавливаете переменную в верхней части вашего JSP (или предпочтительно в шаблоне) и добавляете к ней префикс в качестве контекста.

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

По какой-то причине у меня возникли проблемы с обработкой IE относительных URL-адресов, и мне пришлось вернуться к использованию выражений с переменной JavaScript, заданной в контексте.Я просто разделил свои замены изображений IE на их собственный файл и использовал макросы IE для извлечения правильных.В этом не было ничего особенного, потому что мне все равно уже приходилось это делать, чтобы иметь дело с прозрачными PNG.Это некрасиво, но это работает.

Я использовал большинство из этих методов (за исключением архитектуры XSLT).

Я думаю, что суть (и консенсус) проблемы заключается в наличии сайта с потенциально несколькими каталогами.

Если глубина вашего каталога (за неимением лучшего термина) постоянна, то вы можете полагаться на относительные URL-адреса в таких вещах, как CSS.

Имейте в виду, макет не обязательно должен быть полностью плоским, просто последовательным.

Например, мы создали такие иерархии, как /css, /js, /common, /admin, /user.Размещение соответствующих страниц и ресурсов в соответствующих каталогах.Наличие подобной структуры очень хорошо работает с аутентификацией на основе контейнеров.

Я также сопоставил *.css и * .js с сервлетом JSP и сделал их динамическими, чтобы я мог создавать их "на лету".

Я просто надеялся, что было что-то еще, что я, возможно, пропустил.

Я по НЕТ средства утверждают, что следующее является элегантной проблемой.На самом деле, оглядываясь назад, я бы не рекомендовал эту проблему, учитывая (наиболее вероятное) снижение производительности.

JSP нашего веб-приложения представляли собой строго XML-необработанные данные.Затем эти необработанные данные были отправлены в XSL (на стороне сервера), который применил правильные CSS-теги и выдал XHTML.

У нас был один template.xsl, который будет унаследован несколькими файлами XSL, которые у нас были для разных компонентов веб-сайта.Все наши пути были определены в XSL-файле под названием paths.xml:

<?xml version="1.0" encoding="UTF-8"?>
<paths>
    <path name="account" parent="home">Account/</path>
    <path name="css">css/</path>
    <path name="home">servlet/</path>
    <path name="icons" parent="images">icons/</path>
    <path name="images">images/</path>
    <path name="js">js/</path>
</paths>

Внутренняя ссылка будет содержаться в XML следующим образом:

<ilink name="link to icons" type="icons">link to icons</ilink>

Это будет обработано нашим XSL:

<xsl:template match="ilink">
    <xsl:variable name="temp">
        <xsl:value-of select="$rootpath" />
        <xsl:call-template name="paths">
            <xsl:with-param name="path-name"><xsl:value-of select="@type" /></xsl:with-param>
        </xsl:call-template>
        <xsl:value-of select="@file" />
    </xsl:variable>
        <a href="{$temp}" title="{@name}" ><xsl:value-of select="." /></a>
</xsl:template>

$rootPath был передан в каждый файл с ${applicationScope.contextPath} Идея использования XML вместо простого жесткого кодирования в файле JSP / Java заключалась в том, что мы не хотели перекомпилировать его.

Опять же, решение совсем не хорошее...но однажды мы им воспользовались!

Редактировать:На самом деле, сложность в нашей проблеме возникла из-за того, что мы не смогли использовать JSP для всего нашего представления.Почему бы кому-нибудь просто не использовать ${applicationScope.contextPath} чтобы получить контекстный путь?Тогда у нас это прекрасно сработало.

При создании сайта с нуля я принимаю сторону @Will - стремлюсь к согласованной и предсказуемой структуре URL, чтобы вы могли придерживаться относительных ссылок.

Но ситуация может стать действительно запутанной, если вы обновляете сайт, который изначально был создан для работы непосредственно под корневым каталогом сайта "/" (довольно распространенный для простых сайтов JSP) до официального Java EE упаковка (где корнем контекста будет некоторый путь под корнем).

Это может означать множество изменений в коде.

Если вы хотите избежать или отложить изменения кода, но при этом обеспечить правильную ссылку на корневой контекст, метод, который я протестировал, заключается в использовании фильтров сервлетов.Фильтр может быть перенесен в существующий проект без каких-либо изменений (кроме web.xml) и перенаправит любые ссылки URL в исходящем HTML на правильный путь, а также обеспечит правильную ссылку на перенаправления.

Пример сайта и полезного кода доступен здесь: EnforceContextRootFilter-1.0-src.zip Примечание:фактические правила сопоставления реализованы в виде регулярных выражений в классе servlet и обеспечивают довольно общий охват для всех, но вам может потребоваться изменить их для конкретных обстоятельств.

кстати, я задал немного другой вопрос для решения перенос существующей базы кода из "/" в некорневой контекстный путь

Я обычно пишу свойство как часть моей основной библиотеки JavaScript в моем.Я не думаю, что это идеально, но я думаю, что это лучшее, чего мне удалось достичь.

Во-первых, у меня есть модуль, являющийся частью ядра моего приложения, который всегда доступен

(function (APP) {
  var ctx;
  APP.setContext = function (val) {
    // protect rogue JS from setting the context.
    if (ctx) {
      return;
    }
    val = val || val.trim();
    // Don't allow a double slash for a context.
    if (val.charAt(0) === '/' && val.charAt(1) === '/') {
      return;
    }
    // Context must both start and end in /.
    if (val.length === 0 || val === '/') {
      val = '/';
    } else {
      if (val.charAt(0) !== '/') {
        val = '/' + val;
      }
      if (val.slice(-1) !== '/') {
        val += '/';
      }
    }
    ctx = val;
  };
  APP.getContext = function () {
    return ctx || '/';
  };
  APP.getUrl = function (val) {
    if (val && val.length > 0) {
      return APP.getContext() + (val.charAt(0) === '/' ? val.substring(1) : val);
    }
    return APP.getContext();
  };
})(window.APP = window.APP || {});

Затем я использую apache tiles с общим заголовком, который всегда содержит следующее:

<script type="text/javascript">
  APP.setContext('${pageContext.request['contextPath']}');
  // If preferred use JSTL cor, but it must be available and declared.
  //APP.setContext('<c:url value='/'/>');
</script>

Теперь, когда я инициализировал контекст, я могу использовать getUrl(path) из любого места (js-файлы или внутри jsp / html), который вернет абсолютный путь для заданной входной строки в контексте.

Обратите внимание, что оба приведенных ниже варианта намеренно эквивалентны. getUrl всегда будет возвращать абсолютный путь, поскольку относительный путь не требует от вас знания контекста в первую очередь.

var path = APP.getUrl("/some/path");
var path2 = APP.getUrl("some/path");

Вот лучший способ :Фильтр перенаправления контекста Фильтр должен применяться в точке расширения перед совпадением и, таким образом, использовать аннотацию @PreMatching.

Фильтры, реализующие этот интерфейс, должны быть помечены символом @Provider, чтобы их могла обнаружить среда выполнения JAX-RS.Экземпляры фильтра запроса контейнера также могут быть обнаружены и динамически привязаны к определенным методам ресурсов.

Объясняется с помощью примера кода:

http://writeulearn.com/java-filters/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top