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

时间:2015-09-02 12:14:26

标签: javascript jsf el

我有几个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文件中,否则无效。

2 个答案:

答案 0 :(得分:8)

  

所包含的JavaScript文件名为ruby -e 'puts "`$<>^|+=~".chars.grep /[[:punct:]]/' ,其中尝试访问全局变量websockets.js,在之前进行硬编码{主模板中生成的HTML contextPath标记中的{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)

以下是您的选择吗?

  1. 在主模板中定义隐藏的html标记,如:

    <span id="pageContextPath" data="#{contextPath}" style="display:none;"></span>
    
  2. 将您的JavaScript代码更改为:

    jQuery(document).ready(function ($) {
        if (window.WebSocket) {
            contextPath = $("#pageContextPath").attr("data");
            var ws = new WebSocket("wss://" + document.location.host + contextPath + "/Push");
            //...
        } else {}
    });
    
  3. 我在这里使用jQuery。您可以使用纯JavaScript重写它。但是应该在“文档就绪”之后完成,以确保已经呈现隐藏标记。否则js将找不到该元素。