【问题标题】:Hidden features of JSP/Servlet [closed]JSP/Servlet 的隐藏特性
【发布时间】:2011-02-01 04:18:21
【问题描述】:

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


    【解决方案1】:

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


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

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


    JSP 的预处理请求

    大多数人都知道 Servlet 的 doPost()post - 处理请求(表单提交),但大多数人不知道您可以使用 Servlet 的 doGet() 方法来pre - 处理对 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>
    

    /page(或/page/*)的url-pattern 上映射这样的servlet,只需通过浏览器地址栏或普通链接调用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 中访问它。例如:

    ${bean['class'].name}
    

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

    ${pageContext.session.id}
    

    这将返回 pageContext.getSession().getId() 的值,这在 a.o. 中很有用。 Can an applet communicate with an instance of a servlet.

    ${pageContext.request.contextPath}
    

    这将返回 pageContext.getRequest().getContextPath() 的值,这在 a.o. 中很有用。 How to use relative paths without including the context root name?


    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() 方法(这样你就可以在EL 中通过${entry.key}${entry.value} 访问它)。示例:

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

    参见例如Debugging with jstl - how exactly?


    在 JSP 中获取当前日期

    您可以使用 jsp:useBean 获取当前日期并在 JSTL fmt:formatDate 的帮助下对其进行格式化

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

    此打印(截至目前)如下:“Copyright © 2010”。


    简单友好的网址

    拥有友好 URL 的一种简单方法是利用 HttpServletRequest#getPathInfo() 和隐藏在 /WEB-INF 中的 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。您可以通过拆分/ 上的路径信息更进一步,只将第一部分作为JSP 页面URL,将剩余部分作为“业务操作”(让servlet 充当页面控制器)。另见例如Design Patterns web based applications.


    使用${param} 重新显示用户输入

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

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

    这与request.getParameterMap().get("foo") 基本相同。另见例如How can I retain HTML form field values in JSP after submitting form to Servlet?
    别忘了防止 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}" />
    

    交替 &lt;table&gt; 行与 LoopTagStatus

    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填充列表/数组中的逗号分隔字符串:

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

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

    这会导致类似item1, item2, item3


    EL 函数

    您可以将 public static 实用方法声明为 EL 函数(如 JSTL functions),以便您可以在 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']}
    

    到此为止。也许我迟早会添加更多。

    【讨论】:

    • 这是 Stackoverflow 上最广泛的条目之一。没有你说的技巧,但是任何值得他们盐分的人都应该知道的良好知识和常见做法。对于交替的表格行,最好使用现代 CSS 语法并使用 tr:nth-child(even) 着色,它使您的 HTML 输出更加清晰。
    • 永远完美的 BalusC 先生 :)
    • 哇,非常有用的答案和很好的常见做法,非常感谢 BlausC。
    • 我听说使用日期来获取年份已被弃用。我可以使用日历进行格式化吗?
    • +1 BalusC 继他在 JDK 中使用的设计模式之后,我的第二个最爱。
    猜你喜欢
    • 2019-01-01
    • 1970-01-01
    • 2011-10-25
    • 2011-03-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 2010-09-15
    • 2010-11-07
    相关资源
    最近更新 更多