摆脱在外部 JavaScript 文件中对 Web 应用程序的上下文路径进行硬编码
Get rid of hard-coding the context path of web apps in external JavaScript files
我有几个 WebSockets 端点,例如,
wss://localhost:8181/ContextPath/Push
所有此类端点 URL 都硬编码在单独的外部 JavaScript 文件 (.js
) 中。这些 JavaScript 文件在需要时包含在各自的 XHTML 文件中。主机名和上下文路径应该以编程方式评估,而不是在需要它们的地方进行硬编码。
可以在JavaScript中使用document.location.host
获取主机名(localhost:8181
),但在JavaScript中没有standard/canonical获取上下文的方法应用程序运行的路径。
我正在做类似下面的事情。
全局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
的位置位于 之前 主模板中生成的 HTML <head>
标签中的硬编码 <script>
标签。
换句话说,全局JavaScript变量contextPath
实际上是在被声明之前尝试在包含文件websockets.js
中使用。
无论如何,如何摆脱在外部 JavaScript 文件中对上下文路径进行硬编码?
这样做的唯一目的是与 CSS 文件不同,EL 不在外部 JavaScript 文件中计算。因此,除非将它放在 XHTML 文件中,否则 #{}
不会起作用。
以下是您的选择吗?
在主模板中定义一个隐藏的 html-tag,例如:
<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 重写它。但是应该在"document ready"之后进行,以确保隐藏的标签已经被渲染。否则js找不到那个元素。
What happens is that the included JavaScript file named websockets.js
where the global variable contextPath
is attempted to be accessed, is placed before the hard-coded <script>
tag in the generated HTML <head>
tag in the master template
这是出乎意料的。您在 <h:body>
中用 target="head"
声明了 <h:outputScript>
引用 websockets.js
文件。这应该在 已在 <h:head>
中声明的所有其他脚本资源之后结束。另见 a.o。 How to reference CSS / JS / image resource in Facelets template? After all, this appears to be caused by PrimeFaces bundled HeadRenderer
旨在自动包含一些 CSS 资源并处理 <facet name="first|middle|last">
.
这值得向 PF 人员报告问题(如果尚未完成)。同时,最好的办法是通过在 faces-config.xml
中显式注册 JSF 实现自己的 HeadRenderer
来关闭它(前提是您使用的是 Mojarra)。
<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>
并在 <h:head>
中明确包含 PrimeFaces 主题特定 theme.css
:
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
回到真正的问题,
Anyway, how to get rid of hard-coding the context path in external JavaScript files?
要么将其设置为基本 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");
我有几个 WebSockets 端点,例如,
wss://localhost:8181/ContextPath/Push
所有此类端点 URL 都硬编码在单独的外部 JavaScript 文件 (.js
) 中。这些 JavaScript 文件在需要时包含在各自的 XHTML 文件中。主机名和上下文路径应该以编程方式评估,而不是在需要它们的地方进行硬编码。
可以在JavaScript中使用document.location.host
获取主机名(localhost:8181
),但在JavaScript中没有standard/canonical获取上下文的方法应用程序运行的路径。
我正在做类似下面的事情。
全局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
的位置位于 之前 主模板中生成的 HTML <head>
标签中的硬编码 <script>
标签。
换句话说,全局JavaScript变量contextPath
实际上是在被声明之前尝试在包含文件websockets.js
中使用。
无论如何,如何摆脱在外部 JavaScript 文件中对上下文路径进行硬编码?
这样做的唯一目的是与 CSS 文件不同,EL 不在外部 JavaScript 文件中计算。因此,除非将它放在 XHTML 文件中,否则 #{}
不会起作用。
以下是您的选择吗?
在主模板中定义一个隐藏的 html-tag,例如:
<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 重写它。但是应该在"document ready"之后进行,以确保隐藏的标签已经被渲染。否则js找不到那个元素。
What happens is that the included JavaScript file named
websockets.js
where the global variablecontextPath
is attempted to be accessed, is placed before the hard-coded<script>
tag in the generated HTML<head>
tag in the master template
这是出乎意料的。您在 <h:body>
中用 target="head"
声明了 <h:outputScript>
引用 websockets.js
文件。这应该在 已在 <h:head>
中声明的所有其他脚本资源之后结束。另见 a.o。 How to reference CSS / JS / image resource in Facelets template? After all, this appears to be caused by PrimeFaces bundled HeadRenderer
旨在自动包含一些 CSS 资源并处理 <facet name="first|middle|last">
.
这值得向 PF 人员报告问题(如果尚未完成)。同时,最好的办法是通过在 faces-config.xml
中显式注册 JSF 实现自己的 HeadRenderer
来关闭它(前提是您使用的是 Mojarra)。
<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>
并在 <h:head>
中明确包含 PrimeFaces 主题特定 theme.css
:
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
回到真正的问题,
Anyway, how to get rid of hard-coding the context path in external JavaScript files?
要么将其设置为基本 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");