Enchanced jsp:include implementation
One of the things that has always bothered me about <jsp:include..>
is that it's not possible to pass non-String values into the included page as distinct inputs to the page. For example, I'd like to be able to do the following:
<c:forEach var="item" items="${listOfItems}">
<jsp:include page="mypage.jsp">
<jsp:attribute name="item" value="${item}/>
</jsp:include>
</c:forEach>
The idea being that, by declaring the item being passed in as an attribute inside the jsp:include node, the code is making it clear what the intent was... to supply that parameter to the included page.
The only mechanism currently available for doing this is to define the attribute "globally", and then let the included page read it from the global space. Unfortunately, this looses that same "intent" that using <jsp:param> provides. Additionally, it makes the code harder to debug, for the same reason that globals in any programming environment do.
Does anyone know of an implementation of an include mechanism that performs the functions of jsp:include, but allows for passing in non-String values? Or, if not, I'd be open to alternative ideas that preserve the same goal of intent and ease of debugging.
As a side note, I'd love to see a builtin "catch" mechanism for when the included page throws an error:
<abc:include page="mypage">
<abc:param name="something" value="some string value"/>
<abc:attribu开发者_StackOverflow中文版te name="somethingelse" value="${nonStringValue}"/>
<abc:catch var="ex">
The page failed, so the content it generated will not be sent to the output
stream. Instead, we can collapse the part of the page that it's content
would be... possibly putting a comment in the page with
<!-- There was an exception generating this module:
<c:out value="${ex.getMessage}/>
-->
</abc:catch>
</abc:include>
From the JSTL specification:
Nested scoped variables are only visible within the body of the action and are stored in "page" scope.
You can pass object references via request scope.
standardInclude.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:out value="${foo}" />
standardAction.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
//evil scriptlet
request.setAttribute("mylist", java.util.Arrays.asList("bar", "baz"));
%>
<c:forEach items="${mylist}" var="foo">
<c:set var="foo" scope="request" value="${foo}" />
<jsp:include page="standardInclude.jsp" />
<c:set var="foo" scope="request" value="${null}" />
</c:forEach>
You can use the catch tag to catch exceptions.
thrower.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:out value="OK: ${foo}" />
<%
//evil scriptlet
if ("bar".equals(request.getAttribute("foo")))
throw new java.io.IOException("oops");
%>
catcher.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
//evil scriptlet
request.setAttribute("mylist", java.util.Arrays.asList("bar", "baz"));
%>
<c:forEach items="${mylist}" var="foo">
<br />
<c:set var="foo" scope="request" value="${foo}" />
<c:catch var="ex">
<jsp:include page="thrower.jsp" />
</c:catch>
<c:if test="${ex ne null}">
<c:out value="Error for ${foo}" />
</c:if>
<c:set var="foo" scope="request" value="${null}" />
</c:forEach>
This will emit:
Error for bar
OK: baz
allows for passing in non-String values?
When you are restricted to passing Strings around, a common solution is to Serialize your objects.
Having read the answers here, plus doing additional research on the subject, I've come up with the following:
- You can serialize your objects then deserialize them in the included JSP. Not a fan of this because it makes the code more complex (every object you pass in must be serializable, etc)
- You can use taglibs. Not a fan of this, since I feel they serve a different role than JSP include files
- You can define the variables at the request scope, which will make them available to the included JSP. Not a big fan of this since it doesn't show the intent of the programmer (passing in the values to the included page, for it's use only).
- There really isn't (that I could find) an implementation that achieves what I'm looking for, but it is possibly to build something that comes close via custom tags.
I did some work and put together the code to achieve what I was looking for, and then threw it up on sourceforge. It allows you to specify the inputs in the way I was describing:
<inc:include page="normal.jsp">
<inc:param name="param1" value="param1value" />
<inc:param name="param2" value="param2value" />
<inc:attrib name="attrib1" value="${attrib1value}" />
<inc:attrib name="attrib2" value="${attrib2value}" />
<inc:catch var="ex">
This block was not rolled up because there wasn't an error.
Should never see this, but just in case, the exception was: ${ex.message}
</inc:catch>
</inc:include>
The only problem with it (at this time) is that I add the attributes to the request scope before the page is included, then remove them after (or reset them if they already existed). What I'd like to do, instead, is wrap the request object instead and override the attribute methods to automatically include the values passed in... I'm still working on that.
This can be achived with jsp tag files instead of includes. See my answer to a similar question at JSP Component Creation for details.
精彩评论