Избавьтесь от жесткого кодирования контекстного пути веб-приложений во внешних файлах JavaScript
У меня есть несколько конечных точек WebSockets, таких как,
wss://localhost:8181/ContextPath/Push
Все такие URL-адреса конечных точек жестко запрограммированы в отдельных внешних файлах JavaScript (.js
). Эти файлы JavaScript включены в соответствующие файлы XHTML по мере необходимости. Имя хоста и контекстный путь должны оцениваться программно, а не жестко кодироваться везде, где они требуются.
Имя хоста (localhost:8181
) можно получить в JavaScript, используя document.location.host
но в JavaScript нет стандартного / канонического способа получить контекстный путь, в котором выполняется приложение.
Я делаю что-то вроде следующего.
Глобальная переменная JavaScript объявляется в главном шаблоне следующим образом.
<f:view locale="#{bean.locale}" encoding="UTF-8" contentType="text/html">
<f:loadBundle basename="messages.ResourceBundle" var="messages"/>
<ui:param name="contextPath" value="#{request.contextPath}"/>
<ui:insert name="metaData"></ui:insert>
<h:head>
<script type="text/javascript">var contextPath = "#{contextPath}";</script>
</h:head>
<h:body id="body">
</h:body>
</f:view>
</html>
Файлы JavaScript, в которых имя хоста и путь к контексту жестко заданы, включены в соответствующие клиенты шаблона или в любой из разделов шаблона север, юг, восток и запад следующим образом.
<html lang="#{bean.language}"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:form>
<h:outputScript library="default" name="js/websockets.js" target="head"/>
</h:form>
Только ради точки зрения, websockets.js
выглядит следующим образом (вы можете просто проигнорировать это).
if (window.WebSocket) {
// The global variable "contextPath" is unavailable here
// because it is declared afterwards in the generated HTML.
var ws = new WebSocket("wss://"+document.location.host + contextPath + "/Push");
ws.onmessage = function (event) {
// This handler is invoked, when a message is received through a WebSockets channel.
};
$(window).on('beforeunload', function () {
ws.close();
});
} else {}
Теперь глобальная переменная JavaScript contextPath
объявленный в главном шаблоне, как ожидается, будет доступен во включенном файле JavaScript, а именно websockets.js
, Это, однако, не соответствует действительности.
Что происходит, что включенный файл JavaScript, а именно websockets.js
где глобальная переменная contextPath
пытается получить доступ, помещается перед жестко <script>
тег в сгенерированном HTML <head>
тег в мастер шаблон.
Другими словами, глобальная переменная JavaScript contextPath
на самом деле попытался использовать во включенном файле websockets.js
прежде чем быть объявленным.
В любом случае, как избавиться от жесткого кодирования пути к контексту во внешних файлах JavaScript?
Единственная цель этого состоит в том, чтобы в отличие от CSS-файлов EL не оценивался во внешних JavaScript-файлах. Следовательно, #{}
вещь не будет работать, если она не помещена в файл XHTML.
2 ответа
Что происходит, что включенный файл JavaScript с именем
websockets.js
где глобальная переменнаяcontextPath
пытается получить доступ, помещается перед жестко<script>
тег в сгенерированном HTML<head>
тег в шаблоне мастера
Это неожиданно Вы объявили <h:outputScript>
ссылаясь websockets.js
файл внутри <h:body>
с target="head"
, Это должно закончиться после того, как все другие ресурсы скрипта уже объявлены в <h:head>
, Смотрите также ao Как ссылаться на ресурс CSS / JS / image в шаблоне Facelets? В конце концов, это, кажется, вызвано PrimeFaces в комплекте HeadRenderer
который намерен автоматически включать некоторые CSS-ресурсы и заботиться о <facet name="first|middle|last">
,
Это стоит сообщить о проблеме ребятам из PF (если это еще не сделано). Между тем, вам лучше всего отключить его, явно зарегистрировав собственную реализацию JSF. HeadRenderer
обратно, как показано ниже faces-config.xml
(при условии, что вы используете Мохарру).
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>javax.faces.Head</renderer-type>
<renderer-class>com.sun.faces.renderkit.html_basic.HeadRenderer</renderer-class>
</renderer>
</render-kit>
И явно включить специфическую для PrimeFaces тему theme.css
как показано ниже <h:head>
:
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
Возвращаясь к настоящему вопросу,
В любом случае, как избавиться от жесткого кодирования пути к контексту во внешних файлах JavaScript?
Либо установите его в качестве базового URI (примечание: относительный путь не поддерживается в HTML4 / IE6-8).
<h:head>
<base href="#{request.contextPath}/" />
...
</h:head>
var baseURI = $("base").attr("href");
Или установите его как атрибут данных корневого элемента HTML.
<!DOCTYPE html>
<html lang="en" data-baseuri="#{request.contextPath}/" ...>
...
</html>
var baseURI = $("html").data("baseuri");
Независимо от конкретной проблемы, в качестве совета, чтобы прозрачно охватить как http+ws, так и https + wss, рассмотрите возможность использования location.protocol
вместо жестко wss
,
var ws = new WebSocket(location.protocol.replace("http", "ws") + "//" + location.host + baseURI + "Push");
Является ли следующий вариант для вас?
Определите скрытый html-тег в главном шаблоне, например:
<span id="pageContextPath" data="#{contextPath}" style="display:none;"></span>
Измените код JavaScript на что-то вроде:
jQuery(document).ready(function ($) { if (window.WebSocket) { contextPath = $("#pageContextPath").attr("data"); var ws = new WebSocket("wss://" + document.location.host + contextPath + "/Push"); //... } else {} });
Я использовал здесь JQuery. Вы можете переписать его на простом JavaScript. Но это должно быть сделано после того, как "документ готов", чтобы убедиться, что скрытый тег был обработан. в противном случае JS не найдет этот элемент.