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 © <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:escapeXml
或c:out
(重新)显示用户控制的数据。
<p><input type="text" name="foo" value="${fn:escapeXml(param.foo)}"> <p><c:out value="${bean.userdata}" />
用LoopTagStatus
交替<table>
行
JSTL c:forEach
的varStatus
属性为您提供了一个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']}
这是迄今为止。 也许我会迟早添加一些。