Есть какие-нибудь хитроумные способы обработки контекста в веб-приложении?
-
02-07-2019 - |
Вопрос
В 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.Экземпляры фильтра запроса контейнера также могут быть обнаружены и динамически привязаны к определенным методам ресурсов.
Объясняется с помощью примера кода: