Вопрос-ответ

How can I avoid Java code in JSP files, using JSP 2?

Как я могу избежать Java-кода в файлах JSP, используя JSP 2?

Я знаю, что что-то вроде следующих трех строк

<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>

это старый школьный способ кодирования, и в версии JSP 2 существует метод, позволяющий избежать Java-кода в файлах JSP. Каковы альтернативные строки JSP 2 и как называется этот метод?

Переведено автоматически
Ответ 1

Использование скриптлетов (этих <% %> штучек) в JSP действительно крайне не рекомендуется с момента появления taglibs (например, JSTL) и EL (Языка выражений, этих ${} штучек) еще в 2001 году.

Основными недостатками скриптлетов являются:


  1. Возможность повторного использования: вы не можете повторно использовать скриптлеты.

  2. Заменяемость: вы не можете сделать скриптлеты абстрактными.

  3. OO-возможность: вы не можете использовать наследование / композицию.

  4. Возможность отладки: если скриптлет выдает исключение на полпути, все, что вы получаете, - это пустую страницу.

  5. Тестируемость: скриптлеты не поддаются модульному тестированию.

  6. Ремонтопригодность: согласно 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, но вы также можете легко создавать функции самостоятельно. Вот пример того, как JSTL fn: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>
java jsp