Избавьтесь от жесткого кодирования контекстного пути веб-приложений во внешних файлах 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");

Является ли следующий вариант для вас?

  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 {}
    });
    

Я использовал здесь JQuery. Вы можете переписать его на простом JavaScript. Но это должно быть сделано после того, как "документ готов", чтобы убедиться, что скрытый тег был обработан. в противном случае JS не найдет этот элемент.

Другие вопросы по тегам