Скрытые возможности JSP/Servlet

Меня интересуют ваши трюки и т. Д., Используемые при написании 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 ответ

Решение

Примечание. Мне трудно представить какие-либо "скрытые возможности" для JSP/Servlet. По моему мнению, "лучшие практики" - лучшая формулировка, и я могу думать о любой из них. Это также действительно зависит от вашего опыта работы с JSP/Servlet. После многих лет разработки вы больше не видите этих "скрытых возможностей". В любом случае, я перечислю некоторые из тех маленьких "лучших практик", о которых я за годы узнал, что многие начинающие не полностью осознают это. Они будут классифицированы как "скрытые функции" в глазах многих начинающих. Во всяком случае, вот список:)


Скрыть страницы JSP от прямого доступа

Размещая файлы JSP в /WEB-INF папка, которую вы эффективно скрываете, например, от прямого доступа http://example.com/contextname/WEB-INF/page.jsp, Это приведет к 404, Вы можете получить к ним доступ только через RequestDispatcher в сервлете или используя jsp:include,


Запрос предварительной обработки для JSP

Большинство знает о сервлетах doPost() пост- обработать запрос (отправка формы), но большинство не знает, что вы можете использовать сервлет 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/*) и просто вызвать http://example.com/contextname/page по адресной строке браузера или простой ванильной ссылке, чтобы запустить его. Смотрите также, например, doGet и doPost в Сервлетах.


Динамический включает

Вы можете использовать EL в jsp:include:

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

bean.getPage() можно просто вернуть действительное имя страницы.


EL может получить доступ к любому получателю

EL сам по себе не требует, чтобы объект, к которому нужно получить доступ, был полноценным Javabean. Наличие метода без аргументов с префиксом get или же is более чем достаточно для доступа к нему в EL. Например:

${bean['class'].name}

Это возвращает значение bean.getClass().getName() где getClass() Метод на самом деле наследуется от Object#getClass(), Обратите внимание, что class указывается с помощью "скобки" [] по причинам, указанным здесь, экземпляр проверки на языке выражений EL.

${pageContext.session.id}

Это возвращает значение pageContext.getSession().getId() что полезно в ao. Может ли апплет связываться с экземпляром сервлета.

${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

Ты можешь использовать 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>

Смотрите также, например, Отладка с помощью 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>

Это печатает (на данный момент), как следует: "Copyright © 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/* затем запрос на http://example.com/contextname/pages/foo/bar будет эффективно отображать /WEB-INF/foo/bar.jsp, Вы можете сделать шаг вперед, разделив pathinfo на / и принимать только первую часть как URL-адрес страницы JSP, а остаток - как "бизнес-действия" (пусть сервлет действует как контроллер страницы). См. Также, например, веб-приложения Design Patterns.


Повторно отобразить пользовательский ввод, используя ${param}

Неявный объект EL ${param} что относится к HttpServletRequest#getParameterMap() может использоваться для повторного отображения пользовательского ввода после отправки формы в JSP:

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

Это в основном так же, как request.getParameterMap().get("foo"), См. Также, например, Как я могу сохранить значения полей формы HTML в JSP после отправки формы в сервлет?
Не забудьте предотвратить от 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}" />

чередующийся <table> строки с LoopTagStatus

varStatus атрибут JSTL c:forEach дает вам LoopTagStatus назад, который в свою очередь имеет несколько методов получения (которые могут быть использованы в 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']}

Это было так далеко. Может быть, я добавлю еще рано или поздно.

Другие вопросы по тегам