Любые умные способы обработки контекста в веб-приложении?

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

таким образом myapp.война развертывается вhttp://example.com/myapp.

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

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

однако вы не можете сделать это, скажем, с тегом IMG в HTML. Если вы делаете вы, вероятно, получите 404, потому что то, что вы действительно хотели, было "/myapp/myimage.файл GIF."

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

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

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

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

Что вы делаете?

14 ответов


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

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

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

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

/ css
/ images

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

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

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

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

очевидно, что все это зависит от вашего использования JSPs и JSTL, но я использую JSF с Facelets, и используемые методы похожи - единственная реальная разница-получить корень контекста по-другому.


для HTML-страниц я просто установил HTML <base> тег. Каждая относительная ссылка (т. е. не начинающаяся со scheme или /) станет относительно него. Нет никакого чистого способа схватить его немедленно 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>

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

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

to

<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 folder

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

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

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

Читайте также:


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

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

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


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

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

С сервлет 2.3: новые функции, открытые:

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

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


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

для путей в css-файлах и т. д. Я использую Ant build script что использует сайт.производство.CSS для сайта.css в производственной среде и на сайте.развитие.CSS в encironment развития.

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


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

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

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


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

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

<img src="myimage.gif"/>

вместо

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

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


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

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

/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-адреса в одном и том же плоском пространстве filepath. То есть каждый URL не должен иметь косых черт после /myapp/ .

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

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


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

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

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


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

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

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

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

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

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

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


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

JSPs нашего веб-приложения были строго необработанными XML-данными. Эти необработанные данные затем были отправлены в XSL (на стороне сервера), который применил правильные теги CSS и выплюнул XHTML.

у нас был один шаблон.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 заключалась в том, что мы не хотели перекомпилировать.

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

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


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

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

Это может означать много кода изменения.

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

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

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


я склонен писать свойство как часть моей основной библиотеки 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 с общим заголовком, который всегда содержит следующее:

<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");