Как избежать Java-кода в файлах JSP? - PullRequest
1618 голосов
/ 05 июля 2010

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

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

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

Ответы [ 30 ]

1933 голосов
/ 05 июля 2010

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

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

  1. Возможность повторного использования: вы не можете повторно использовать скриптлеты.
  2. Возможность замены: Вы не можете сделать скриптлеты абстрактными.
  3. OO-способность: вы не можете использовать наследование / композицию.
  4. Отладка: если скриптлет выбрасывает исключение на полпути, все, что вы получаете - это пустая страница.
  5. Тестируемость: скриптлеты не тестируются на юнитах.
  6. Ремонтопригодность: за один раз требуется больше времени для поддержания логики смешанного / загроможденного / дублированного кода.

Sun Сам Oracle также рекомендует в соглашениях по кодированию JSP избегать использования скриптлетов всякий раз, когда те же функции возможны с помощью (тегов) классов. Вот несколько цитируемых ссылок:

Из спецификации JSP 1.2 настоятельно рекомендуется использовать в вашем веб-приложении стандартную библиотеку тегов JSP (JSTL), чтобы помочь уменьшить потребность в JSP-скриптлетах на ваших страницах. Страницы, использующие JSTL, в общем, легче читать и поддерживать.

...

Где возможно, избегать JSP скриптлетов всякий раз, когда библиотеки тегов предоставляют эквивалентную функциональность. Это облегчает чтение и обслуживание страниц, помогает отделить бизнес-логику от логики представления и упрощает превращение ваших страниц в страницы в стиле JSP 2.0 (спецификация JSP 2.0 поддерживает, но не подчеркивает использование скриптлетов).

...

В духе принятия шаблона проектирования модель-представление-контроллер (MVC) для уменьшения связи между уровнем представления и бизнес-логикой, JSP-скриптлеты не следует использовать для написания бизнес-логики. Скорее, JSP скриптлеты используются при необходимости для преобразования данных (также называемых «объектами значений»), возвращаемых при обработке клиентских запросов, в надлежащий формат, готовый для клиента. Даже тогда это лучше сделать с помощью сервлета фронт-контроллера или пользовательского тега.


Как заменить скриптлетов полностью зависит от единственной цели кода / логики. Чаще всего этот код помещается в полноценный класс Java:

  • Если вы хотите вызывать тот же Java-код при каждом запросе, меньше или больше независимо от запрашиваемой страницы, например, проверяя, вошел ли пользователь в систему, затем внедрите фильтр и напишите код соответствующим образом в методе doFilter(). E.g.:

    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-код для предварительной обработки запроса, например, предварительно загрузив некоторый список из базы данных для отображения в некоторой таблице, при необходимости, основываясь на некоторых параметрах запроса, затем реализуйте servlet и напишите код соответствующим образом в методе doGet(). E.g.:

    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-код для постпроцесс запроса, например, обрабатывая отправку формы, затем реализуйте сервлет и напишите код соответствующим образом в методе doPost(). E.g.:

    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} в 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 для доступа и отображения"backend »внутри страницы JSP, тогда вам нужно использовать EL (Expression Language), эти ${} вещи.Например, повторное отображение введенных входных значений:

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

    ${param.foo} отображает результат request.getParameter("foo").


  • Если вы хотите вызвать некоторые utility 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.Преемник JSP Facelets уже неявно экранировал HTML, поэтому вам не нужно беспокоиться о дырах XSS в Facelets.

См. Также:

219 голосов
/ 15 августа 2011

В качестве меры предосторожности: отключить сценарии для пользы

Поскольку обсуждается еще один вопрос , вы можете и всегда должны отключать сценарии в своем дескрипторе веб-приложения web.xml.

Я всегда делал бы это, чтобы не допустить добавления разработчиками скриплетов, особенно в крупных компаниях, где вы рано или поздно потеряете обзор.Настройки web.xml выглядят так:

<jsp-config>
  <jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
     <scripting-invalid>true</scripting-invalid>
  </jsp-property-group>
</jsp-config>
107 голосов
/ 05 июля 2010

JSTL предлагает теги для условных выражений, циклов, множеств, наборов и т. Д. Например:

<c:if test="${someAttribute == 'something'}">
   ...
</c:if>

JSTL работает с атрибутами запроса - они чаще всего задаются в запросе сервлетом, который перенаправляет в JSP.

60 голосов
/ 05 июля 2010

Я не уверен, правильно ли я понял.

Вы должны прочитать кое-что о MVC. Spring MVC & Распорки 2 являются двумя наиболее распространенными решениями.

53 голосов
/ 05 июля 2010

Вы можете использовать теги 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>
34 голосов
/ 22 марта 2011

Существуют также компоненты на основе компонентов, такие как Wicket , которые генерируют для вас много HTML-кода. Тэги, которые заканчиваются в HTML, являются чрезвычайно простыми, и в них практически отсутствует смешанная логика. В результате получаются почти пустые HTML-страницы с типичными HTML-элементами. Недостатком является то, что в Wicket API есть много компонентов для изучения, и некоторые вещи могут быть труднодостижимы при этих ограничениях.

32 голосов
/ 04 февраля 2011

В архитектурном шаблоне MVC JSP представляют слой View.Встраивание кода Java в JSP считается плохой практикой.Вы можете использовать JSTL , freeMarker , скорость с JSP в качестве «движка шаблонов».Поставщик данных для этих тегов зависит от фреймворков , с которыми вы имеете дело.Struts 2 и webwork в качестве реализации для MVC Pattern использует OGNL"очень интересный метод представления свойств Beans для JSP".

27 голосов
/ 05 июля 2010

Опыт показывает, что у JSP есть некоторые недостатки, одним из которых является трудность избежать смешивания разметки с реальным кодом.

Если можете, подумайте об использовании специализированной технологии для того, что вам нужно сделать. В Java EE 6 есть JSF 2.0, который предоставляет множество полезных функций, включая склейку Java-бинов вместе со страницами JSF с помощью подхода #{bean.method(argument)}.

26 голосов
/ 11 мая 2012

Если вы просто хотите избежать недостатков Java-кодирования в JSP, вы можете сделать это даже с помощью скриптов.Просто следуйте некоторой дисциплине, чтобы иметь минимальный Java в JSP и почти нет вычислений и логики на странице JSP.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%//instantiate a JSP controller
MyController clr = new MyController(request, response);

//process action if any
clr.process(request);

//process page forwaring if necessary

//do all variable assignment here
String showMe = clr.getShowMe();%>

<html>
    <head>
    </head>
    <body>
        <form name="frm1">
            <p><%= showMe %>
            <p><% for(String str : clr.listOfStrings()) { %>
            <p><%= str %><% } %>

            // and so on   
        </form>
    </body>
</html>
26 голосов
/ 18 августа 2011

Узнайте, как настроить и написать свои собственные теги, используя JSTL

Обратите внимание, что EL равен EviL (исключения времени выполнения, рефакторинг)
Калитка тоже может быть злой (производительность, трудоемкая для небольших приложений или простой уровень просмотра)

Пример из java2s ,

Это необходимо добавить в веб-приложение web.xml

<taglib>
    <taglib-uri>/java2s</taglib-uri>
    <taglib-location>/WEB-INF/java2s.tld</taglib-location>
</taglib>

создать файл: java2s.tld в / WEB-INF /

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<!-- a tab library descriptor -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>Java2s Simple Tags</short-name>

    <!-- this tag manipulates its body content by converting it to upper case
    -->
    <tag>
        <name>bodyContentTag</name>
        <tag-class>com.java2s.BodyContentTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
          <name>howMany</name>
        </attribute>
    </tag>
</taglib>

скомпилируйте следующий код в WEB-INF \ classes \ com \ java2s

package com.java2s;

import java.io.IOException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class BodyContentTag extends BodyTagSupport{
    private int iterations, howMany;

    public void setHowMany(int i){
        this.howMany = i;
    }

    public void setBodyContent(BodyContent bc){
        super.setBodyContent(bc);
        System.out.println("BodyContent = '" + bc.getString() + "'");
    }

    public int doAfterBody(){
        try{    
            BodyContent bodyContent = super.getBodyContent();
            String bodyString  = bodyContent.getString();
            JspWriter out = bodyContent.getEnclosingWriter();

            if ( iterations % 2 == 0 ) 
                out.print(bodyString.toLowerCase());
            else
                out.print(bodyString.toUpperCase());

            iterations++;
            bodyContent.clear(); // empty buffer for next evaluation
        }
        catch (IOException e) {
            System.out.println("Error in BodyContentTag.doAfterBody()" + e.getMessage());
            e.printStackTrace();
        } // end of catch

        int retValue = SKIP_BODY;

        if ( iterations < howMany ) 
            retValue = EVAL_BODY_AGAIN;

        return retValue;
    }
}

Запустите сервер и загрузите bodyContent.jsp в браузер

<%@ taglib uri="/java2s" prefix="java2s" %>
<html>
    <head>
        <title>A custom tag: body content</title>
    </head>
    <body>
        This page uses a custom tag manipulates its body content.Here is its output:
        <ol>
            <java2s:bodyContentTag howMany="3">
            <li>java2s.com</li>
            </java2s:bodyContentTag>
        </ol>
    </body>
</html>
...