JSP / Servlet的隐藏功能


77

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

Answers:


155

注意:我发现很难想到JSP / Servlet的任何“隐藏功能”。我认为“最佳做法”是一个更好的措辞,我可以想到其中的任何一种。这实际上还取决于您对JSP / Servlet的经验。经过多年的发展,您不再看到那些“隐藏的功能”。无论如何,我将列出一些小小的“最佳实践”,这些年来,我发现许多入门者并未完全意识到这一点。在许多入门者看来,这些将被归类为“隐藏功能”。无论如何,这是列表:)


隐藏JSP页面以防止直接访问

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


预处理JSP请求

大多数人都知道ServletdoPost()可以对请求(表单提交)进行处理,但是大多数人不知道您可以使用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映射url-pattern/page(或/page/*)上,然后http://example.com/contextname/page通过浏览器地址栏或简单的普通链接调用即可运行它。另请参见例如Servlet中的doGet和doPost


动态包含

您可以在jsp:include以下位置使用EL :

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

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


EL可以访问任何吸气剂

EL本身并不要求要访问的对象是完全有价值的Javabean。存在无前缀方法的前缀get或前缀is足以在EL中访问该方法。例如:

${bean['class'].name}

这将返回方法实际继承自的bean.getClass().getName()位置的值。请注意,出于此处提到的原因,使用“大括号符号”指定了EL表达式语言中的instanceof checkgetClass()Object#getClass()class[]

${pageContext.session.id}

这将返回其值,pageContext.getSession().getId()该值在小程序可以与servlet实例进行通信中很有用。

${pageContext.request.contextPath}

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


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

您也可以使用c:forEach来迭代Map。每次迭代都给出一个Map.Entry具有getKey()getValue()方法的,这样您就可以通过${entry.key}和在EL中对其进行访问${entry.value}。例:

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

另请参见例如使用jstl进行调试-究竟如何?


在JSP中获取当前日期

您可以jsp:useBean使用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);
}

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


使用以下命令重新显示用户输入 ${param}

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

<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}" />

<table>与交替行LoopTagStatus

varStatusJSTL的属性c:forEach为您提供了一个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

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

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

结果就是这样item1, item2, item3


EL功能

您可以将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和查询字符串

如果已转发JSP,则可以通过以下方式获取原始请求URL:

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

和原始请求查询字符串,

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

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


20
这是Stackoverflow上最广泛的条目之一。正如您所说的,没有花招,但是值得任何食盐的人都应该了解良好的知识和常规。对于交替表行,更好的方法是使用现代CSS语法并使用tr:nth-​​child(even)进行着色,这会使您的HTML输出更加清晰。
Photodeus

6
永远完美的BalusC先生:)
穆罕默德·休伊迪

1
太棒了,提供了很多有用的答案和良好的常规做法,这比BlausC多得多。
palAlaa 2010年

我听说使用Date获取年份已过时。我可以使用日历进行格式化吗?
itsraja 2011年

1
+1是继BalusC在JDK中使用的“设计模式”之后的第二个最爱。
zawhtut
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.