Как я могу избежать Java-кода в файлах JSP, используя JSP 2?
Я знаю, что что-то вроде следующих трех строк
<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>
это старый школьный способ кодирования, и в версии JSP 2 существует метод, позволяющий избежать Java-кода в файлах JSP. Каковы альтернативные строки JSP 2 и как называется этот метод?
Переведено автоматически
Ответ 1
Использование скриптлетов (этих <% %>
штучек) в JSP действительно крайне не рекомендуется с момента появления taglibs (например, JSTL) и EL (Языка выражений, этих ${}
штучек) еще в 2001 году.
Основными недостатками скриптлетов являются:
- Возможность повторного использования: вы не можете повторно использовать скриптлеты.
- Заменяемость: вы не можете сделать скриптлеты абстрактными.
- OO-возможность: вы не можете использовать наследование / композицию.
- Возможность отладки: если скриптлет выдает исключение на полпути, все, что вы получаете, - это пустую страницу.
- Тестируемость: скриптлеты не поддаются модульному тестированию.
- Ремонтопригодность: согласно saldo, требуется больше времени для поддержания логики смешанного / загроможденного / дублированного кода.
Солнце Сам Oracle также рекомендует в соглашениях о кодировании JSP избегать использования скриптлетов всякий раз, когда такая же функциональность возможна с помощью классов (тегов). Вот несколько актуальных примеров:
Из спецификации JSP 1.2 настоятельно рекомендуется использовать стандартную библиотеку тегов JSP (JSTL) в вашем веб-приложении, чтобы помочь уменьшить потребность в скриптлетах JSP на ваших страницах. Страницы, использующие JSTL, в целом легче читать и поддерживать.
...
По возможности, избегайте скриптлетов JSP всякий раз, когда библиотеки тегов предоставляют эквивалентную функциональность. Это упрощает чтение и обслуживание страниц, помогает отделить бизнес-логику от логики представления и упростит превращение ваших страниц в страницы в стиле JSP 2.0 (спецификация JSP 2.0 поддерживает, но не подчеркивает использование скриптлетов).
...
В духе принятия шаблона проектирования model-view-controller (MVC) для уменьшения связи между уровнем представления и бизнес-логикой, скриптлеты JSP не следует использовать для написания бизнес-логики. Скорее, скриптлеты JSP используются при необходимости для преобразования данных (также называемых "объектами значений"), возвращаемых в результате обработки запросов клиента, в надлежащий формат, готовый для клиента. Даже в этом случае это было бы лучше сделать с помощью сервлета front controller или пользовательского тега.
Способ замены скриптлетов полностью зависит от единственной цели кода / логики. Чаще всего этот код должен быть помещен в полноценный класс Java:
Если вы хотите вызывать один и тот же Java-код при каждом запросе, меньше или больше, независимо от запрашиваемой страницы, например, проверяя, вошел ли пользователь в систему, тогда реализуйте фильтр и напишите соответствующий код в
doFilter()
методе. Например.:public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page.
} else {
chain.doFilter(request, response); // Logged in, just continue request.
}
}При отображении на соответствующем,
<url-pattern>
охватывающем интересующие страницы JSP, вам не нужно копировать один и тот же фрагмент кода на всех страницах JSP.Если вы хотите вызвать некоторый Java-код для обработки запроса GET, например, предварительно загрузить некоторый список из базы данных для отображения в некоторой таблице, при необходимости на основе некоторых параметров запроса, затем реализуйте сервлет и напишите соответствующий код в
doGet()
методе. Например.:protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
List<Product> products = productService.list(); // Obtain all products.
request.setAttribute("products", products); // Store products in request scope.
request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table.
} catch (SQLException e) {
throw new ServletException("Retrieving products failed!", e);
}
}Таким образом, проще работать с исключениями. Доступ к базе данных осуществляется не в разгар рендеринга JSP, а задолго до отображения JSP. У вас по-прежнему есть возможность изменять ответ всякий раз, когда доступ к БД выдает исключение. В приведенном выше примере будет отображаться страница с ошибкой 500 по умолчанию, которую вы в любом случае можете настроить с помощью
<error-page>
вweb.xml
.Если вы хотите вызвать некоторый Java-код для обработки POST-запроса, например, сбора данных из отправленной HTML-формы и выполнения с ними некоторых бизнес-операций (преобразование, проверка, сохранение в БД и т.д.), Затем реализуйте сервлет и напишите соответствующий код в
doPost()
методе. Например.:protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userService.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
response.sendRedirect("home"); // Redirect to home page.
} else {
request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope.
request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error.
}
}Таким образом, проще работать с разными целевыми страницами результатов: повторно отображать форму с ошибками проверки в случае ошибки (в этом конкретном примере вы можете повторно отобразить ее, используя
${message}
in EL) или просто перейти на нужную целевую страницу в случае успеха.Если вы хотите вызвать некоторый Java-код для управления планом выполнения и / или местом назначения запроса и ответа, затем реализуйте сервлет в соответствии с шаблоном Front Controller в MVC. Например.:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
} else {
response.sendRedirect(view);
}
} catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}Или просто принять MVC-фреймворк, такой как JSF, Spring MVC, Wicket и т.д., Чтобы в итоге у вас была только страница JSP / Facelets и класс JavaBean без необходимости в пользовательском сервлете.
Если вы хотите вызвать некоторый Java-код для управления потоком внутри страницы JSP, то вам нужно использовать (существующий) taglib для управления потоком, такой как JSTL core. Например. отображение
List<Product>
в таблице:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<table>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.name}</td>
<td>${product.description}</td>
<td>${product.price}</td>
</tr>
</c:forEach>
</table>С тегами в стиле XML, которые прекрасно вписываются во весь этот HTML, код лучше читается (и, следовательно, лучше обслуживается), чем куча скриптлетов с различными открывающими и закрывающими фигурными скобками ("Где, черт возьми, эта закрывающая фигурная скобка принадлежит?"). Простой способ - настроить ваше веб-приложение на выдачу исключения всякий раз, когда скриптлеты все еще используются, добавив следующую часть в
web.xml
:<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>В Facelets, преемнике JSP, который является частью предоставляемого Java EE MVC-фреймворка JSF, уже невозможно использовать скриптлеты. Таким образом, вы автоматически будете вынуждены делать все "правильно".
Если вы хотите вызвать некоторый Java-код для доступа и отображения "внутренних" данных внутри страницы JSP, то вам нужно использовать EL (язык выражений), те
${}
вещи. Например. повторное отображение отправленных входных значений:<input type="text" name="foo" value="${param.foo}" />
В
${param.foo}
отображается результатrequest.getParameter("foo")
.Если вы хотите вызвать какую-либо утилиту Java-кода непосредственно на странице JSP (обычно
public static
методы), то вам нужно определить их как EL-функции. В JSTL есть стандартные функции taglib, но вы также можете легко создавать функции самостоятельно. Вот пример того, как JSTLfn:escapeXml
полезен для предотвращения XSS-атак.<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
...
<input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />Обратите внимание, что чувствительность XSS никоим образом не связана конкретно с Java / JSP / JSTL / EL / чем угодно, эту проблему необходимо учитывать в каждом веб-приложении, которое вы разрабатываете. Проблема скриптлетов заключается в том, что он не предоставляет способа встроенных предупреждений, по крайней мере, не используя стандартный Java API. Facelets-преемник JSP уже имеет неявное экранирование HTML, поэтому вам не нужно беспокоиться о дырах XSS в Facelets.
Смотрите также:
Ответ 2
В качестве меры предосторожности: отключите скриптлеты навсегда
Поскольку обсуждается другой вопрос, вы можете и всегда должны отключать скриптлеты в вашем web.xml
дескрипторе веб-приложения.
Я бы всегда так делал, чтобы ни один разработчик не добавлял скриптлеты, особенно в крупных компаниях, где вы рано или поздно потеряете обзор. web.xml
Настройки выглядят следующим образом:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>
Ответ 3
JSTL предлагает теги для условных выражений, циклов, наборов, gets и т.д. Например:
<c:if test="${someAttribute == 'something'}">
...
</c:if>
JSTL работает с атрибутами запроса - чаще всего они задаются в запросе сервлетом, который пересылает JSP.
Ответ 4
Вы можете использовать теги JSTL вместе с выражениями EL, чтобы избежать смешивания кода Java и HTML:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
</head>
<body>
<c:out value="${x + 1}" />
<c:out value="${param.name}" />
// and so on
</body>
</html>