Is there an JSTL EL (Expression Language) Equivale

2019-02-16 19:27发布

问题:

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.

回答1:

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.



回答2:

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>


回答3:

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.



标签: jsp jstl el