JSP / Servlet的隐藏功能

时间:2010-03-26 13:12:58

标签: jsp servlets

我对编写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>

1 个答案:

答案 0 :(得分:152)

注意:我发现很难想到JSP / Servlet的任何“隐藏功能”。在我看来,“最佳实践”是一种更好的措辞,我可以想到它们中的任何一种。它还取决于您使用JSP / Servlet的经验。经过多年的发展,你再也看不到那些“隐藏的特征”了。无论如何,我会列出一些我最近发现的那些小“最佳实践”,许多初学者都没有完全意识到这一点。在许多初学者的眼中,这些将被归类为“隐藏的特征”。无论如何,这是列表:)


隐藏直接访问的JSP页面

通过将JSP文件放在/WEB-INF文件夹中,您可以有效地将其隐藏起来,例如http://example.com/contextname/WEB-INF/page.jsp。这将导致404。然后,您只能通过Servlet中的RequestDispatcherjsp: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>

url-pattern /page(或/page/*)上映射此类servlet,只需通过浏览器地址栏或普通的vanilla链接调用http://example.com/contextname/page即可运行它。另见例如doGet and doPost in Servlets


动态包括

您可以在jsp:include中使用EL:

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

bean.getPage()只能返回有效的网页名称。


EL可以访问任何getter

EL本身并不要求要访问的对象是 fullworthy Javabean。存在以getis为前缀的无参数方法足以在EL中访问它。 E.g:

${bean['class'].name}

这将返回bean.getClass().getName()的值,其中getClass()方法实际上是从Object#getClass()继承的。请注意class是使用“括号”[]指定的,原因是此处instanceof check in EL expression language

${pageContext.session.id}

这将返回{。3}}的值,该值在a.o中很有用。 pageContext.getSession().getId()

${pageContext.request.contextPath}

这将返回{。3}}的值,该值在a.o中很有用。 Can an applet communicate with an instance of a servlet


EL也可以访问地图

以下EL符号

${bean.map.foo}

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

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

解析为bean.getMap().get("foo.bar")。如果你想要一个动态密钥,也可以使用括号表示法,但是不加引号:

${bean.map[otherbean.key]}

解析为bean.getMap().get(otherbean.getKey())


使用JSTL

迭代Map

您也可以使用pageContext.getRequest().getContextPath()来迭代Map。每次迭代都会提供How to use relative paths without including the context root name?,而c:forEach又有getKey()getValue()方法(这样您就可以通过${entry.key}${entry.value}在EL中访问它了。例如:

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

参见例如Map.Entry


在JSP中获取当前日期

您可以使用Debugging with jstl - how exactly?获取当前日期,并在JSTL jsp:useBean

的帮助下对其进行格式化
<jsp:useBean id="date" class="java.util.Date" />
...
<p>Copyright &copy; <fmt:formatDate value="${date}" pattern="yyyy" /></p>

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


简单易用的网址

拥有友好网址的一种简单方法是使用隐藏在/WEB-INF中的fmt:formatDate和JSP:

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充当页面控制器 )。另见例如HttpServletRequest#getPathInfo()


使用${param}

重新显示用户输入

引用Design Patterns web based applications的隐式EL对象${param}可用于在JSP中提交表单后重新显示用户输入:

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

这与request.getParameterMap().get("foo")基本相同。另见例如HttpServletRequest#getParameterMap()
不要忘记防止XSS!见下一章。


JSTL以防止XSS

要阻止您的网站How can I retain HTML form field values in JSP after submitting form to Servlet?,您需要做的就是使用JSTL XSSfn:escapeXml(重新)显示用户控制的数据。

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

使用<table>

替换LoopTagStatus

JSTL c:outvarStatus属性为您提供c:forEach后退,而后者又有几种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中的逗号字符串

另一个有用的LoopTagStatus方法是LoopTagStatus

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

结果类似于item1, item2, item3


EL功能

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

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和查询字符串

如果已转发JSP,则可以通过

获取原始请求URL
${requestScope['javax.servlet.forward.request_uri']} 

和原始请求查询字符串,

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

到目前为止。也许我迟早会加一些。