Any clever ways of handling the context in a web app?

前端 未结 13 1459
一整个雨季
一整个雨季 2020-12-02 13:37

In Java, web apps are bundled in to WARs. By default, many servlet containers will use the WAR name as the context name for the application.

Thus myapp.war gets depl

相关标签:
13条回答
  • 2020-12-02 13:38

    When creating a site from scratch, I side with @Will - aim for a consistent and predictable url structure so that you can stick with relative references.

    But things can get really messy if you are updating a site that was originally built to work directly under the site root "/" (pretty common for simple JSP sites) to formal Java EE packaging (where context root will be some path under the root).

    That can mean a lot of code changes.

    If you want to avoid or postpone the code changes, but still ensure correct context root referencing, a technique I've tested is to use servlet filters. The filter can be dropped into an existing proejct without changing anything (except web.xml), and will remap any url references in the outbound HTML to the correct path, and also ensure redirects are correctly referenced.

    An example site and usable code available here: EnforceContextRootFilter-1.0-src.zip NB: the actual mapping rules are implemented as regex in the servlet class and provide a pretty general catch-all - but you may need to modify for particular circumstances.

    btw, I forked a slightly different question to address migrating existing code base from "/" to a non-root context-path

    0 讨论(0)
  • 2020-12-02 13:39

    For HTML pages, I just set the HTML <base> tag. Every relative link (i.e. not starting with scheme or /) will become relative to it. There is no clean way to grab it immediately by HttpServletRequest, so we need little help of JSTL here.

    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <c:set var="req" value="${pageContext.request}" />
    <c:set var="url">${req.requestURL}</c:set>
    <c:set var="uri">${req.requestURI}</c:set>
    
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <base href="${fn:substring(url, 0, fn:length(url) - fn:length(uri))}${req.contextPath}/" />
        <link rel="stylesheet" href="css/default.css">
        <script src="js/default.js"></script>
      </head>
      <body>
        <img src="img/logo.png" />
        <a href="other.jsp">link</a>
      </body>
    </html>
    

    This has in turn however a caveat: anchors (the #identifier URL's) will become relative to the base path as well. If you have any of them, you would like to make it relative to the request URL (URI) instead. So, change like

    <a href="#identifier">jump</a>
    

    to

    <a href="${uri}#identifier">jump</a>
    

    In JS, you can just access the <base> element from the DOM whenever you'd like to convert a relative URL to an absolute URL.

    var base = document.getElementsByTagName("base")[0].href;
    

    Or if you do jQuery

    var base = $("base").attr("href");
    

    In CSS, the image URLs are relative to the URL of the stylesheet itself. So, just drop the images in some folder relative to the stylesheet itself. E.g.

    /css/style.css
    /css/images/foo.png
    

    and reference them as follows

    background-image: url('images/foo.png');
    

    If you rather like to drop the images in some folder at same level as CSS folder

    /css/style.css
    /images/foo.png
    

    then use ../ to go to the common parent folder

    background-image: url('../images/foo.png');
    

    See also:

    • Is it recommended to use the <base> html tag?
    0 讨论(0)
  • 2020-12-02 13:45

    Vilmantas said the right word here: relative URLs.

    All you need to do in your IMG is to use

    <img src="myimage.gif"/>
    

    instead of

    <img src="/myimage.gif"/>
    

    and it'll be relative to the app context (as the browser is interpreting the URL to go to)

    0 讨论(0)
  • 2020-12-02 13:45

    I by no means claim that the following is an elegant issue. In fact, in hindsight, I wouldn't recommend this issue given the (most likely) performance hit.

    Our web app's JSPs were strictly XML raw data. This raw data was then sent into an XSL (server-side) which applied the right CSS tags, and spit out the XHTML.

    We had a single template.xsl which would be inherited by the multiple XSL files that we had for different components of the website. Our paths were all defined in an XSL file called paths.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <paths>
        <path name="account" parent="home">Account/</path>
        <path name="css">css/</path>
        <path name="home">servlet/</path>
        <path name="icons" parent="images">icons/</path>
        <path name="images">images/</path>
        <path name="js">js/</path>
    </paths>
    

    An internal link would be in the XML as follows:

    <ilink name="link to icons" type="icons">link to icons</ilink>
    

    This would get processed by our XSL:

    <xsl:template match="ilink">
        <xsl:variable name="temp">
            <xsl:value-of select="$rootpath" />
            <xsl:call-template name="paths">
                <xsl:with-param name="path-name"><xsl:value-of select="@type" /></xsl:with-param>
            </xsl:call-template>
            <xsl:value-of select="@file" />
        </xsl:variable>
            <a href="{$temp}" title="{@name}" ><xsl:value-of select="." /></a>
    </xsl:template>
    

    $rootPath was passed onto each file with ${applicationScope.contextPath} The idea behind us using XML instead of just hard-coding it in a JSP/Java file was we didn't want to have to recompile.

    Again, the solution isn't a good one at all...but we did use it once!

    Edit: Actually, the complexity in our issue arose because we weren't able to use JSPs for our entire view. Why wouldn't someone just use ${applicationScope.contextPath} to retrieve the context path? It worked fine for us then.

    0 讨论(0)
  • 2020-12-02 13:46

    I tend to write a property as part of my core JavaScript library within my. I don't think it's perfect but I think it's the best I've managed to achieve.

    First off, I have a module that's part of my app core that is always available

    (function (APP) {
      var ctx;
      APP.setContext = function (val) {
        // protect rogue JS from setting the context.
        if (ctx) {
          return;
        }
        val = val || val.trim();
        // Don't allow a double slash for a context.
        if (val.charAt(0) === '/' && val.charAt(1) === '/') {
          return;
        }
        // Context must both start and end in /.
        if (val.length === 0 || val === '/') {
          val = '/';
        } else {
          if (val.charAt(0) !== '/') {
            val = '/' + val;
          }
          if (val.slice(-1) !== '/') {
            val += '/';
          }
        }
        ctx = val;
      };
      APP.getContext = function () {
        return ctx || '/';
      };
      APP.getUrl = function (val) {
        if (val && val.length > 0) {
          return APP.getContext() + (val.charAt(0) === '/' ? val.substring(1) : val);
        }
        return APP.getContext();
      };
    })(window.APP = window.APP || {});
    

    I then use apache tiles with a common header that always contains the following:

    <script type="text/javascript">
      APP.setContext('${pageContext.request['contextPath']}');
      // If preferred use JSTL cor, but it must be available and declared.
      //APP.setContext('<c:url value='/'/>');
    </script>
    

    Now that I've initialized the context I may use getUrl(path) from anywhere (js files or within jsp/html) which will return an absolute path for the given input string within the context.

    Note that the following are both equivalent intentionally. getUrl will always return an absolute path as a relative path does not need you to know the context in the first place.

    var path = APP.getUrl("/some/path");
    var path2 = APP.getUrl("some/path");
    
    0 讨论(0)
  • 2020-12-02 13:48

    You can use JSTL for creating urls.

    For example, <c:url value="/images/header.jpg" /> will prefix the context root.

    With CSS, this usually isn't an issue for me.

    I have a web root structure like this:

    /css
    /images

    In the CSS file, you then just need to use relative URLs (../images/header.jpg) and it doesn't need to be aware of the context root.

    As for JavaScript, what works for me is including some common JavaScript in the page header like this:

    <script type="text/javascript">
    var CONTEXT_ROOT = '<%= request.getContextPath() %>';
    </script>
    

    Then you can use the context root in all your scripts (or, you can define a function to build paths - may be a bit more flexible).

    Obviously this all depends on your using JSPs and JSTL, but I use JSF with Facelets and the techniques involved are similar - the only real difference is getting the context root in a different way.

    0 讨论(0)
提交回复
热议问题