摆脱外部JavaScript文件中Web应用程序的上下文路径的硬编码

Tin*_*iny 4 javascript jsf el

我有几个WebSockets端点,如,

wss://localhost:8181/ContextPath/Push
Run Code Online (Sandbox Code Playgroud)

所有这些端点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>
Run Code Online (Sandbox Code Playgroud)

其中主机名和上下文路径被硬编码的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>
Run Code Online (Sandbox Code Playgroud)

仅为了观点,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 {}
Run Code Online (Sandbox Code Playgroud)

现在,contextPath主模板中声明的全局JavaScript变量应该在包含的JavaScript文件中可用websockets.js.然而,这是不真实的.

所发生的是包含的JavaScript文件,即尝试访问websockets.js全局变量的contextPath位置,放在主模板中生成的HTML 标记中的硬编码标记之前.<script><head>

换句话说,全局JavaScript变量contextPath实际上是websockets.js在声明之前尝试在包含的文件中使用.

无论如何,如何摆脱外部JavaScript文件中的硬编码上下文路径?

这样做的唯一目的是,与CSS文件不同,EL不在外部JavaScript文件中进行评估.因此,#{}除非将其放在XHTML文件中,否则它将无法工作.

Bal*_*usC 8

所发生的是,包含的JavaScript文件命名为尝试访问websockets.js全局变量的contextPath位置,它位于主模板中生成的HTML 标记中的硬编码标记之前<script><head>

这是出乎意料的.你声明的<h:outputScript>引用websockets.js内部文件<h:body>target="head".这应该已经声明的所有其他脚本资源之后结束<h:head>.另请参阅ao 如何在Facelets模板中引用CSS/JS /图像资源?毕竟,这似乎是由捆绑的PrimeFaces引起的,HeadRenderer它意图自动包含一些CSS资源并照顾好<facet name="first|middle|last">.

这对PF人来说是值得的问题报告(如果还没有完成的话).同时,最好的办法是通过显式注册JSF实现自己的HeadRenderer后面来关闭它faces-config.xml(如果您使用的是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>
Run Code Online (Sandbox Code Playgroud)

并明确包括PrimeFaces主题特定theme.css如下<h:head>:

<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
Run Code Online (Sandbox Code Playgroud)

回到真正的问题,

无论如何,如何摆脱外部JavaScript文件中的硬编码上下文路径?

将其设置为基本URI(注意:HTML4/IE6-8中不支持相对路径).

<h:head>
    <base href="#{request.contextPath}/" />
    ...
</h:head>
Run Code Online (Sandbox Code Playgroud)
var baseURI = $("base").attr("href");
Run Code Online (Sandbox Code Playgroud)

或者将其设置为HTML根元素的数据属性.

<!DOCTYPE html>
<html lang="en" data-baseuri="#{request.contextPath}/" ...>
    ...
</html>
Run Code Online (Sandbox Code Playgroud)
var baseURI = $("html").data("baseuri");
Run Code Online (Sandbox Code Playgroud)

具体问题无关,作为建议,透明地涵盖http + ws和https + wss,考虑使用location.protocol而不是硬编码wss.

var ws = new WebSocket(location.protocol.replace("http", "ws") + "//" + location.host + baseURI + "Push");
Run Code Online (Sandbox Code Playgroud)