ChatGPT解决这个技术问题 Extra ChatGPT

How do you get the contextPath from JavaScript, the right way?

Using a Java-based back-end (i.e., servlets and JSP), if I need the contextPath from JavaScript, what is the recommended pattern for doing that, any why? I can think of a few possibilities. Am I missing any?

1. Burn a SCRIPT tag into the page that sets it in some JavaScript variable

<script>var ctx = "<%=request.getContextPath()%>"</script>

This is accurate, but requires script execution when loading the page.

2. Set the contextPath in some hidden DOM element

<span id="ctx" style="display:none;"><%=request.getContextPath()%></span>

This is accurate, and doesn't require any script execution when loading the page. But you do need a DOM query when need to access the contextPath. The result of the DOM query can be cached if you care that much about performance.

3. Try to figure it out within JavaScript by examining document.URL or the BASE tag

function() {
    var base = document.getElementsByTagName('base')[0];
    if (base && base.href && (base.href.length > 0)) {
        base = base.href;
    } else {
        base = document.URL;
    }
    return base.substr(0,
        base.indexOf("/", base.indexOf("/", base.indexOf("//") + 2) + 1));
};

This doesn't require any script execution when loading the page, and you can also cache the result if necessary. But this only works if you know your context path is a single directory -- as opposed to the root directory (/) or the multiple directories down (/mypath/iscomplicated/).

Which way I'm leaning

I'm favoring the hidden DOM element, because it doesn't require JavaScript code execution at the load of the page. Only when I need the contextPath, will I need to execute anything (in this case, run a DOM query).

I can for life not understand why "but requires script execution when loading the page" is such a big problem that you'd prefer to fallback to DOM fiddling/traversing. Can you please elaborate? Also, what exactly do you need to context path for? Perhaps there are other ways so that you don't need it at all.
JavaScript execution while loading the page blocks parallel downloads. Maybe it doesn't matter with a one-liner. DOM traversal might be more penalizing, but at least you have the option to delay the penalty till you absolutely need it (if you need it). I'm not sure that a lookup by ID would be all that penalizing either.
I don't need the contextPath myself. I was just bringing up a "what if". Here's a possible example: I'm loading a JS file dynamically and need to know its path. I can use an absolute path if I know the contextPath ahead of time, but if the contextPath changes, then my scripts break. I can use a relative path, but that's dependent on the location of the parent HTML doc. If I move the file, my page breaks. Knowing the contextPath, I can protect myself from these types of breakages by having a dynamically-generated absolute path.
Assigning a global variable is basically a no-op in modern browsers. I would alone not use old fashioned scriptlets for this, but just EL. var ctx = '${pageContext.request.contextPath}';
@BalusC You are amazing. Thank you for always posting what you know about how to work with JSPs. Also, I can't get your suggestion to work. When I put it in a script tag, the EL is not recognized. And when I don't put it in a script tag, the error says that a variable is defined wrong. How do I do what you posted above?

M
Mike M. Lin

Based on the discussion in the comments (particularly from BalusC), it's probably not worth doing anything more complicated than this:

<script>var ctx = "${pageContext.request.contextPath}"</script>

Although tedious, I always preferred to have my JavaScript objects receive a property that defines the "baseUrl" or context. I would have a script tag in the jsp to "initialize" my JavaScript objects for the page, and pass the baseUrl into them there. This way I don't have to assume that some global variable is available.
@Mike M. Lin , Are there any pitfalls if we use the option fetching the path using document.url
can someone help me? I added the global variable in my javascript file but it is returning me the raw String "${pageContext.request.contextPath}" and not the contextPath of project..
@FelipeMosso It goes into your .jsp file, not your JavaScript file.
@MikeM.Lin thank you, I will try it later.. the tags made me think I should add into the JavaScript file
C
Cedric Simon

Got it :D

function getContextPath() {
   return window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
}
alert(getContextPath());

Important note: Does only work for the "root" context path. Does not work with "subfolders", or if context path has a slash ("/") in it.


This is the best answer! It doesn't mix jsp with javascript definitions
FWIW this fails if the context path has slash in it (e.g. /foo/bar).
I'm seeing window.location.pathname with a value like "/context/p1/p2/p3.html", so this suggestion returns "/context". But a Java web application may be deployed in the ROOT context, so the first component of the pathname is not a context string. I don't know a way to handle this using client-only script, which is a problem as I'm not using JSP or Servlets.
This does not return the context path. This returns the first path name of the URL which is not necessarily the context path.
I'm facing this problem for last two days.Finally I find the solution of that issue. Thanks a lot @Cedric
佚名

I think you can achieve what you are looking for by combining number 1 with calling a function like in number 3.

You don't want to execute scripts on page load and prefer to call a function later on? Fine, just create a function that returns the value you would have set in a variable:

function getContextPath() {
   return "<%=request.getContextPath()%>";
}

It's a function so it wont be executed until you actually call it, but it returns the value directly, without a need to do DOM traversals or tinkering with URLs.

At this point I agree with @BalusC to use EL:

function getContextPath() {
   return "${pageContext.request.contextPath}";
}

or depending on the version of JSP fallback to JSTL:

function getContextPath() {
   return "<c:out value="${pageContext.request.contextPath}" />";
}

Cool idea, but this would actually be more overhead. The because now I have a function declaration in addition to the variable assignment. function getContextPath() {..} actually expands to window.getContextPath = function() {..}. At this point I agree with BalusC that there's nothing wrong with something like var ctx = '${pageContext.request.contextPath}';. It is about as close to a no-op as you can get.
A
Armando Cordova

Reviewer the solution by this Checking the solution of this page, make the following solution I hope it works: Example:

Javascript:

var context = window.location.pathname.substring(0, window.location.pathname.indexOf("/",2)); 
var url =window.location.protocol+"//"+ window.location.host +context+"/bla/bla";

O
Oleg Shavrov

I render context path to attribute of link tag with id="contextPahtHolder" and then obtain it in JS code. For example:

<html>
    <head>
        <link id="contextPathHolder" data-contextPath="${pageContext.request.contextPath}"/>
    <body>
        <script src="main.js" type="text/javascript"></script>
    </body>
</html>

main.js

var CONTEXT_PATH = $('#contextPathHolder').attr('data-contextPath');
$.get(CONTEXT_PATH + '/action_url', function() {});

If context path is empty (like in embedded servlet container istance), it will be empty string. Otherwise it contains contextPath string


Nice solution, works for me in my case of a Grails app and a gsp snippet like this: ${request.contextPath}
This work for me using springboot with thymeleaf, made small modification. In the HTML THEN IN JS var CONTEXT_PATH = $('#contextPathHolder').attr('data-contextPath'); $.get(CONTEXT_PATH + 'action_url', function() {});
M
Michael Hegner

A Spring Boot with Thymeleaf solution could look like:

Lets say my context-path is /app/

In Thymeleaf you can get it via:

<script th:inline="javascript">
    /*<![CDATA[*/
        let contextPath    = /*[[@{/}]]*/
    /*]]>*/
</script>