Is there an JSTL EL (Expression Language) Equivalent to <c:url/>
Asked Answered
P

3

8

We use the tomcat urlrewrite plugin to rewrite outbound URLs as well as inbound ones. To do this, you need to use the JSTL tag.

This works great for clean urls and i18n, however it yields ugly code, including tags-within-tags, like this:

<link href='<c:url value="/content/bootstrap/css/bootstrap.css" />' rel="stylesheet" />

or:

<a href='<c:url value="/nextPage.jsp"/>' />Next Page</a>

One alternative is to use a variable, like this:

<c:url value="/nextPage.jsp" var="nextPageUrl"/>
<a href='${nextPageUrl}' />Next Page</a>

This is really clean, but verbose.

Is there an expression-language friendly way to do this?

I was sort of hoping for something like:

<a href='${url "/nextPage.jsp}' />Next Page</a>

Thanks.

Pase answered 31/5, 2012 at 19:49 Comment(2)
${pageContext.request.contextPath}/nextPage.jsp ? Won't work for outbound, but it makes links within the application pretty clean.Flin
that doesn't solve the goal though. the point is to run the url through the whole outbound link formatting logic Tomcat provides, and any filters you've installed, like urlrewrite. In this case we are doing something more complicated than just absolute paths, we were re-writing js files to include the build version for cache busting.Pase
P
0

The solution I finally came up with was to use custom tags to solve the problem for CSS and JS. A similar solution could be applied to tags too of course, although I haven't had the need yet.

<my:csslink url="/css/custom.css" />
<my:script url="/script/myscript.js" />

The CSS tag looks like this, the Javascript one is obviously very similar:

<%@tag language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@attribute name="url" required="true" type="java.lang.String" description="the absolute (to the webapp) path of the css file"%>
<c:url var="encUrl" value='${url}' />
<link href='${encUrl}' rel='stylesheet' />

Don't forget to import the tags in your jsp:

<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>

The custom EL solution would be cleaner is some circumstances, but if you want to add support for additional parameters or attributes, a tag is probably the way to go.

Pase answered 21/12, 2012 at 16:18 Comment(0)
P
2

There is nothing out of the box, but nothing prevents you to write your own EL functions, and thus use something like that:

<a href='${myFn:url("/nextPage.jsp")}' />Next Page</a>

I don't feel that it's more readable than the standard c:url tag, and it would be even worse if it has to accept parameter names and values, though.

See http://docs.oracle.com/javaee/1.4/tutorial/doc/JSPIntro7.html for how to define and use EL functions.

Palaver answered 31/5, 2012 at 19:56 Comment(1)
Thanks. My goal was to avoid having nested tags. Nested tags confuse IDEs and are pretty nasty to read through. I agree the above isn't super clear either, but its better than a tag. I'll look into it, thanks.Pase
I
1

Where are several approaches.

1) Bind base to variable in common fragment:

common.jspf:

<%@tag language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:url value="/" var="base"/>

or like:

<c:set var="base" value="${pageContext.request.contextPath}"/>

and include fragment in each page:

<%@include file="base.jspf"%>
<script src="${base}/js/jquery.js"></script>
<link href="${base}/css/bootstrap.css" rel="stylesheet" type="text/css">
<a href="${base}/index.html">Home</a>

2) Use ${pageContext.request.contextPath} everywhere:

<script src="${pageContext.request.contextPath}/js/jquery.js"></script>
<link href="${pageContext.request.contextPath}/css/bootstrap.css" rel="stylesheet" type="text/css">
<a href="${pageContext.request.contextPath}/index.html">Home</a>

3) is my favorite: add filter that include base for attribute:

/src/java/company/project/web/filter/BaseFilter.java

import java.io.IOException;
import javax.servlet.*;

/**
 * Make sane JSP, instead of:
 *   <pre>&lt;a href="&lt;c:url value='/my/path/${id}.html'/&gt;"&gt;Title&lt;/a&gt;</pre>
 * allow to use:
 *   <pre>&lt;a href="${ctx}/my/path/${id}.html"&gt;Title&lt;/a&gt;</pre>
 */
public class BaseFilter implements Filter {

    @Override
    public void init(FilterConfig fc) {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setAttribute("base", request.getServletContext().getContextPath());
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() { }

}

and register that filter in web.xml:

<filter>
    <filter-name>BaseFilter</filter-name>
    <filter-class>company.project.web.filter.BaseFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>BaseFilter</filter-name>
    <url-pattern>*.htm</url-pattern>
    <url-pattern>*.json</url-pattern>
</filter-mapping>

and use clean syntax (like above but without need to include boilerplate fragments):

<script src="${base}/js/jquery.js"></script>
<link href="${base}/css/bootstrap.css" rel="stylesheet" type="text/css">
<a href="${base}/index.html">Home</a>

NOTE I set additional attributes in that filter, for example to switch between development and minified version of CSS/JS:

private String min;

@Override
public void init(FilterConfig fc) {
    min = fc.getServletContext().getInitParameter("min");
    if (min == null)
        min = fc.getInitParameter("min");
    if (min == null)
        min = "min";
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    request.setAttribute("ctx", request.getServletContext().getContextPath());
    request.setAttribute("min", min);
    chain.doFilter(request, response);
}

and corresponding JSP code:

<script src="${base}/js/jquery.${min}.js"></script>
<link href="${base}/css/bootstrap.${min}.css" rel="stylesheet" type="text/css">

Initial parameter min may set to dev or min in externally to WAR file in contex deployment descriptor with fall-back to minified version when not set.

4) Use a scriplets:

<a href="<%=request.getContextPath()%>/index.html">Home</a>
Irvine answered 26/10, 2015 at 21:33 Comment(0)
P
0

The solution I finally came up with was to use custom tags to solve the problem for CSS and JS. A similar solution could be applied to tags too of course, although I haven't had the need yet.

<my:csslink url="/css/custom.css" />
<my:script url="/script/myscript.js" />

The CSS tag looks like this, the Javascript one is obviously very similar:

<%@tag language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@attribute name="url" required="true" type="java.lang.String" description="the absolute (to the webapp) path of the css file"%>
<c:url var="encUrl" value='${url}' />
<link href='${encUrl}' rel='stylesheet' />

Don't forget to import the tags in your jsp:

<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>

The custom EL solution would be cleaner is some circumstances, but if you want to add support for additional parameters or attributes, a tag is probably the way to go.

Pase answered 21/12, 2012 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.