Приховані функції JSP / сервлету [закрито]


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>

Відповіді:


155

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


Приховати сторінки 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/*) і просто Invoke http://example.com/contextname/pageпо адресному рядку браузера або проста ваніль посилання , щоб запустити його. Див. Також, наприклад, doGet та doPost у сервлетах .


Динамічний включає

Ви можете використовувати EL в jsp:include:

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

bean.getPage()Можу просто повернути дійсні ІмяСтраніци.


EL може отримати доступ до будь-якого геттера

EL не вимагає, щоб об'єкт, до якого потрібно отримати доступ, був повноцінним Javabean. Наявність методу no-arg, який має префікс 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ітерацію над a 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та відформатувати за допомогою JSTLfmt: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, а залишок як "ділові дії" (нехай сервлет виступає в ролі контролера сторінки ). Див. Також, наприклад, веб-додатки для дизайну .


Повторно відобразити введення користувача за допомогою ${param}

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

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

Це в основному робить те саме, що request.getParameterMap().get("foo"). Див. Також, наприклад, Як я можу зберегти значення полів форми HTML у JSP після подання форми в Servlet?
Не забудьте запобігти 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']}

На цьому все було. Можливо, я рано чи пізно додам ще трохи.


20
Це один із найширших записів тут на Stackoverflow. Ніяких хитрощів, як ви сказали, але хороших знань та загальноприйнятих практик повинен знати кожен, хто вартий їх солі. Для змінних рядків таблиці краще використовувати сучасний синтаксис CSS і розфарбувати за допомогою tr: nth-child (even), це робить ваш вихід HTML ще чистішим.
Фотодей

6
Завжди ідеальний пан БалусC :)
Мухаммед Хеді

1
Ого, оооочень інформативна відповідь та чудові загальноприйняті практики, так багато подяки BlausC.
palAlaa

Я чув, що використання дати для отримання року застаріло. Чи можу я використовувати Календар для форматування?
ітраджа

1
+1 Мій другий фаворит від BalusC після його шаблону дизайну, використаного в JDK.
zawhtut
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.