JSP / Servlet的隐藏function

我对编写JSP / Servlet时使用的技巧感兴趣。 我将开始:

我最近发现了如何在一个标签的属性中包含一个JSP标签的输出:

<c:forEach items="${items}"> <jsp:attribute name="var"> <mytag:doesSomething/> </jsp:attribute> <jsp:body> <%-- when using jsp:attribute the body must be in this tag --%> </jsp:body> </c:forEach> 

注意:我很难想到JSP / Servlet的任何“隐藏function”。 在我看来,“最佳实践”是一个更好的措辞,我可以想到其中的任何一个。 这也取决于你使用JSP / Servlet的经验。 经过多年的发展,你不再看到这些“隐藏的function”。 无论如何,我会列举一些我以前多年来发现许多先发者都没有完全意识到的“最佳实践”。 这些将在许多首发者的眼中被归类为“隐藏的特征”。 无论如何,这是列表:)


隐藏JSP页面直接访问

通过将JSP文件放置在/WEB-INF文件夹中,可以有效地将它们隐藏起来,例如http://example.com/contextname/WEB-INF/page.jsp 。 这将导致404 。 然后,您只能通过Servlet中的RequestDispatcher或使用jsp:include来访问它们。


JSP的预处理请求

大多数人都知道Servlet的doPost() 处理请求(表单提交),但大多数人不知道你可以使用Servlet的doGet()方法预处理 JSP的请求。 例如:

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Item> items = itemDAO.list(); request.setAttribute("items", items); request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response); } 

用于预加载一些表格数据,这些数据在JSTL的c:forEach帮助下显示:

 <table> <c:forEach items="${items}" var="item"> <tr><td>${item.id}</td><td>${item.name}</td></tr> </c:forEach> </table> 

将这样的servlet映射到/page (或/page/* )的url-pattern ,然后通过浏览器地址栏或普通的vanilla链接调用http://example.com/contextname/page来运行它。 另请参阅,例如Servlet中的doGet和doPost 。


dynamic包括

你可以在jsp:include使用EL jsp:include

 <jsp:include page="/WEB-INF/${bean.page}.jsp" /> 

bean.getPage()可以返回一个有效的页面名称。


EL可以访问任何getter

EL本身并不要求要被访问的对象是一个完整的 Javabean。 在前面加上get或者is的无参数方法的存在足以在EL中访问它。 例如:

 ${bean['class'].name} 

这将返回bean.getClass().getName()的值,其中getClass()方法实际上是从Object#getClass()inheritance的。 请注意, class是使用“括号符号” []指定的原因在这里提到的instanceof检查在ELexpression语言 。

 ${pageContext.session.id} 

这将返回pageContext.getSession().getId()的值pageContext.getSession().getId()在ao中很有用一个applet可以和一个servlet的实例进行通信 。

 ${pageContext.request.contextPath} 

这将返回pageContext.getRequest().getContextPath()在ao中有用的值如何使用相对path而不包括上下文根名称?


EL也可以访问地图

以下EL符号

 ${bean.map.foo} 

parsing成bean.getMap().get("foo") 。 如果Map键包含一个点,则可以使用带引号的键“括号表示法” []

 ${bean.map['foo.bar']} 

它parsing为bean.getMap().get("foo.bar") 。 如果你想要一个dynamic的键,也可以使用大括号,但是不带引号:

 ${bean.map[otherbean.key]} 

它parsing为bean.getMap().get(otherbean.getKey())


用JSTL迭代Map

你也可以使用c:forEach遍历一个Map 。 每个迭代都提供一个Map.Entry ,它又有getKey()getValue()方法(这样你就可以通过${entry.key}${entry.value} )在EL中访问它。 例:

 <c:forEach items="${bean.map}" var="entry"> Key: ${entry.key}, Value: ${entry.value} <br> </c:forEach> 

另请参阅例如使用jstl进行debugging – 具体如何?


在JSP中获取当前date

您可以使用jsp:useBean获取当前的date,并使用JSTL fmt:formatDate

 <jsp:useBean id="date" class="java.util.Date" /> ... <p>Copyright &copy; <fmt:formatDate value="${date}" pattern="yyyy" /></p> 

这打印(截至目前)如下:“版权所有©2010”。


简单友好的url

有一个简单的友好URL的方法是利用HttpServletRequest#getPathInfo()和JSP隐藏在/WEB-INF

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF" + request.getPathInfo() + ".jsp").forward(request, response); } 

如果您将此servlet映射到/pages/* ,则http://example.com/contextname/pages/foo/bar上的请求将有效显示/WEB-INF/foo/bar.jsp 。 你可以通过在/上分割pathinfo来进一步做一步,只把第一部分作为JSP页面的URL,剩余部分作为“业务操作”(让servlet充当页面控制器 )。 另请参阅devise模式基于Web的应用程序 。


使用${param}重新显示用户input

引用HttpServletRequest#getParameterMap()的隐式EL对象${param}可用于在JSP中提交表单之后重新显示用户input:

 <input type="text" name="foo" value="${param.foo}"> 

这基本上和request.getParameterMap().get("foo") 。 请参阅例如在将表单提交给Servlet之后,如何在JSP中保留HTML表单域值?
不要忘记阻止XSS! 请参阅以下章节。


JSTL防止XSS

为了防止您的网站从XSS ,所有您需要做的就是使用JSTL fn:escapeXmlc:out (重新)显示用户控制的数据。

 <p><input type="text" name="foo" value="${fn:escapeXml(param.foo)}"> <p><c:out value="${bean.userdata}" /> 

LoopTagStatus交替<table>

JSTL c:forEachvarStatus属性为您提供了一个LoopTagStatus ,后者又有几个getter方法(可以在EL中使用!)。 因此,要检查偶数行,只需检查loop.getIndex() % 2 == 0

 <table> <c:forEach items="${items}" var="item" varStatus="loop"> <tr class="${loop.index % 2 == 0 ? 'even' : 'odd'}">...</tr> <c:forEach> </table> 

这将有效地结束

 <table> <tr class="even">...</tr> <tr class="odd">...</tr> <tr class="even">...</tr> <tr class="odd">...</tr> ... </table> 

使用CSS来给他们不同的背景颜色。

 tr.even { background: #eee; } tr.odd { background: #ddd; } 

使用LoopTagStatus从List / Array填充逗号分隔的string:

另一个有用的LoopTagStatus方法是isLast()

 <c:forEach items="${items}" var="item" varStatus="loop"> ${item}${!loop.last ? ', ' : ''} <c:forEach> 

这导致类似item1, item2, item3


ELfunction

您可以将public static实用程序方法声明为EL函数(如JSTL函数 ),以便您可以在EL中使用它们。 例如

 package com.example; public final class Functions { private Functions() {} public static boolean matches(String string, String pattern) { return string.matches(pattern); } } 

/WEB-INF/functions.tld ,如下所示:

 <?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>Custom_Functions</short-name> <uri>http://example.com/functions</uri> <function> <name>matches</name> <function-class>com.example.Functions</function-class> <function-signature>boolean matches(java.lang.String, java.lang.String)</function-signature> </function> </taglib> 

可以用作

 <%@taglib uri="http://example.com/functions" prefix="f" %> <c:if test="${f:matches(bean.value, '^foo.*')}"> ... </c:if> 

获取原始请求URL和查询string

如果JSP已经被转发,你可以通过获取原始的请求URL,

 ${requestScope['javax.servlet.forward.request_uri']} 

和原来的请求查询string通过,

 ${requestScope['javax.servlet.forward.query_string']} 

这是迄今为止。 也许我会迟早添加一些。