我有几个WebSockets端点,例如,
wss://localhost:8181/ContextPath/Push
所有此类端点URL都在单独的外部JavaScript文件(.js
)中进行硬编码。这些JavaScript文件在需要时包含在各自的XHTML文件中。应该以编程方式评估主机名和上下文路径,而不是在需要它们的地方进行硬编码。
主机名(localhost:8181
)可以使用document.location.host
在JavaScript中获取,但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
,将置于硬编码{{1}之前标记在主模板中生成的HTML <script>
标记中。
换句话说,全局JavaScript变量<head>
实际上是在声明之前尝试在包含的文件contextPath
中使用。
无论如何,如何摆脱硬编码外部JavaScript文件中的上下文路径?
这样做的唯一目的是,与CSS文件不同,EL不会在外部JavaScript文件中进行评估。因此,websockets.js
除非将其置于XHTML文件中,否则无效。
答案 0 :(得分:8)
所包含的JavaScript文件名为
ruby -e 'puts "`$<>^|+=~".chars.grep /[[:punct:]]/'
,其中尝试访问全局变量websockets.js
,在之前进行硬编码{主模板中生成的HTMLcontextPath
标记中的{1}}标记
这是出乎意料的。您使用<script>
在<head>
内声明了<h:outputScript>
引用websockets.js
文件。这应该是之后的 已经在<h:body>
中声明的所有其他脚本资源。另见a.o. How to reference CSS / JS / image resource in Facelets template?毕竟,这似乎是由捆绑的HeadRenderer
PrimeFaces引起的,该版本旨在自动包含一些CSS资源并负责target="head"
。
这对PF家伙来说是值得的问题报告(如果还没有完成的话)。同时,最好的办法是通过在<h:head>
中明确注册JSF实现自己的<facet name="first|middle|last">
来关闭它(前提是您正在使用Mojarra)。 / p>
HeadRenderer
明确在faces-config.xml
中明确包含PrimeFaces主题特定<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>
:
theme.css
回到真正的问题,
无论如何,如何摆脱硬编码外部JavaScript文件中的上下文路径?
将其设置为基本URI(注意:HTML4 / IE6-8中不支持相对路径)。
<h:head>
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
或者将其设置为HTML根元素的数据属性。
<h:head>
<base href="#{request.contextPath}/" />
...
</h:head>
var baseURI = $("base").attr("href");
无关具体问题,作为建议,透明地涵盖http + ws和https + wss,请考虑使用location.protocol
而不是硬编码<!DOCTYPE html>
<html lang="en" data-baseuri="#{request.contextPath}/" ...>
...
</html>
var baseURI = $("html").data("baseuri");
答案 1 :(得分:2)
以下是您的选择吗?
在主模板中定义隐藏的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将找不到该元素。