如何避免 JSP 文件中的 Java 代码?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3177733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:06:55  来源:igfitidea点击:

How to avoid Java code in JSP files?

javajspscriptlet

提问by chmoelders

I'm new to Java EE and I know that something like the following three lines

我是 Java EE 的新手,我知道类似于以下三行

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

is an old school way of coding and in JSP version 2 there exists a method to avoid Java code in JSP files. Can someone please tell me the alternative JSP 2 lines, and what this technique is called?

是一种老派的编码方式,在 JSP 版本 2 中存在一种避免在 JSP 文件中使用 Java 代码的方法。有人可以告诉我替代的 JSP 2 行,以及这种技术叫什么?

采纳答案by BalusC

The use of scriptlets(those <% %>things) in JSPis indeed highly discouraged since the birth of taglibs(like JSTL) and EL(Expression Language, those ${}things) way back in 2001.

自从 2001 年taglib(如JSTL)和EL表达式语言,那些东西)诞生以来,在JSP 中使用scriptlet(那些<% %>东西)确实是非常不鼓励的。${}

The major disadvantages of scriptletsare:

Scriptlet的主要缺点是:

  1. Reusability:you can't reuse scriptlets.
  2. Replaceability:you can't make scriptlets abstract.
  3. OO-ability:you can't make use of inheritance/composition.
  4. Debuggability:if scriptlet throws an exception halfway, all you get is a blank page.
  5. Testability:scriptlets are not unit-testable.
  6. Maintainability:per saldo more time is needed to maintain mingled/cluttered/duplicated code logic.
  1. 可重用性:您不能重用 scriptlet。
  2. 可替换性:你不能让 scriptlet 抽象。
  3. 面向对象的能力:你不能使用继承/组合。
  4. 可调试性:如果 scriptlet 中途抛出异常,你得到的只是一个空白页面。
  5. 可测试性:scriptlet 不可单元测试。
  6. 可维护性:每个saldo需要更多时间来维护混合/混乱/重复的代码逻辑。

SunOracle itself also recommends in the JSP coding conventionsto avoid use of scriptletswhenever the same functionality is possible by (tag) classes. Here are several cites of relevance:

SunOracle 本身也建议在JSP 编码约定中,只要(标记)类可以实现相同的功能,就应避免使用scriptlet。这里有几个相关的引用:

From JSP 1.2 Specification, it is highly recommended that the JSP Standard Tag Library (JSTL) be used in your web application to help reduce the need for JSP scriptletsin your pages. Pages that use JSTL are, in general, easier to read and maintain.

...

Where possible, avoid JSP scriptletswhenever tag libraries provide equivalent functionality. This makes pages easier to read and maintain, helps to separate business logic from presentation logic, and will make your pages easier to evolve into JSP 2.0-style pages (JSP 2.0 Specification supports but de-emphasizes the use of scriptlets).

...

In the spirit of adopting the model-view-controller (MVC) design pattern to reduce coupling between the presentation tier from the business logic, JSP scriptlets should not be usedfor writing business logic. Rather, JSP scriptlets are used if necessary to transform data (also called "value objects") returned from processing the client's requests into a proper client-ready format. Even then, this would be better done with a front controller servlet or a custom tag.

根据 JSP 1.2 规范,强烈建议在 Web 应用程序中使用 JSP 标准标记库 (JSTL),以帮助减少页面中对 JSP 脚本的需求。使用 JSTL 的页面通常更易于阅读和维护。

...

只要标记库提供等效功能,就应尽可能避免使用 JSP scriptlet。这使页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分开,并使您的页面更容易演变为 JSP 2.0 样式的页面(JSP 2.0 规范支持但不强调使用 scriptlet)。

...

本着采用模型-视图-控制器 (MVC) 设计模式来减少表示层与业务逻辑之间的耦合的精神,不应使用 JSP 脚本来编写业务逻辑。相反,如果有必要,使用 JSP scriptlet 将处理客户端请求返回的数据(也称为“值对象”)转换为适当的客户端就绪格式。即便如此,最好使用前端控制器 servlet 或自定义标记来完成。



How to replace scriptletsentirely depends on the sole purpose of the code/logic. More than often this code is to be placed in a fullworthy Java class:

如何替换scriptlet完全取决于代码/逻辑的唯一目的。这段代码经常被放置在一个完整的 Java 类中:

  • If you want to invoke the sameJava code on everyrequest, less-or-more regardless of the requested page, e.g. checking if a user is logged in, then implement a filterand write code accordingly in doFilter()method. 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.
        }
    }
    

    When mapped on an appropriate <url-pattern>covering the JSP pages of interest, then you don't need to copypaste the same piece of code overall JSP pages.


  • If you want to invoke some Java code to preprocessa request, e.g. preloading some list from a database to display in some table, if necessary based on some query parameters, then implement a servletand write code accordingly in doGet()method. 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);
        }
    }
    

    This way dealing with exceptions is easier. The DB is not accessed in the midst of JSP rendering, but far before the JSP is been displayed. You still have the possibility to change the response whenever the DB access throws an exception. In the above example, the default error 500 page will be displayed which you can anyway customize by an <error-page>in web.xml.


  • If you want to invoke some Java code to postprocessa request, e.g. processing a form submit, then implement a servletand write code accordingly in doPost()method. 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.
        }
    }
    

    This way dealing with different result page destinations is easier: redisplaying the form with validation errors in case of an error (in this particular example you can redisplay it using ${message}in EL), or just taking to the desired target page in case of success.


  • If you want to invoke some Java code to controlthe execution plan and/or the destination of the request and the response, then implement a servletaccording to the MVC's Front Controller Pattern. E.g.:

    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);
        }
    }
    

    Or just adopt an MVC framework like JSF, Spring MVC, Wicket, etc so that you end up with just a JSP/Facelets page and a JavaBean class without the need for a custom servlet.


  • If you want to invoke some Java code to control the flowinside a JSP page, then you need to grab an (existing) flow control taglib like JSTL core. E.g. displaying List<Product>in a table:

    <%@ 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>
    

    With XML-style tags which fit nicely among all that HTML, the code is better readable (and thus better maintainable) than a bunch of scriptlets with various opening and closing braces ("Where the heck does this closing brace belong to?"). An easy aid is to configure your web application to throw an exception whenever scriptletsare still been used by adding the following piece to web.xml:

    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <scripting-invalid>true</scripting-invalid>
        </jsp-property-group>
    </jsp-config>
    

    In Facelets, the successor of JSP, which is part of the Java EE provided MVC framework JSF, it is already notpossible to use scriptlets. This way you're automatically forced to do things "the right way".


  • If you want to invoke some Java code to access and display"backend" data inside a JSP page, then you need to use EL (Expression Language), those ${}things. E.g. redisplaying submitted input values:

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

    The ${param.foo}displays the outcome of request.getParameter("foo").


  • If you want to invoke some utilityJava code directly in the JSP page (typically public staticmethods), then you need to define them as EL functions. There's a standard functions taglibin JSTL, but you can also easily create functions yourself. Here's an example how JSTL fn:escapeXmlis useful to prevent XSSattacks.

    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    ...
    <input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
    

    Note that the XSS sensitivity is in no way specifically related to Java/JSP/JSTL/EL/whatever, this problem needs to be taken into account in everyweb application you develop. The problem of scriptletsis that it provides no way of builtin preventions, at least not using the standard Java API. JSP's successor Facelets has already implicit HTML escaping, so you don't need to worry about XSS holes in Facelets.

  • 如果您想在每个请求上调用相同的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代码来预处理一个请求,例如从数据库中预加载一些列表以显示在一些表中,如果需要的话基于一些查询参数,然后实现一个servlet并在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 呈现过程中不会访问 DB,而是远在 JSP 显示之前。每当数据库访问引发异常时,您仍然可以更改响应。在上面的示例中,将显示默认的错误 500 页面,您可以通过<error-page>in自定义该页面web.xml


  • 如果你想调用一些 Java 代码来对请求进行后处理,例如处理表单提交,那么实现一个servlet并在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}EL 中使用重新显示它),或者在成功的情况下直接转到所需的目标页面。


  • 如果你想调用一些 Java 代码来控制执行计划和/或请求和响应的目的地,那么根据MVC 的 Front Controller Pattern实现一个servlet。例如:

    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);
        }
    }
    

    或者只是采用JSFSpring MVCWicket等MVC 框架,这样您最终就可以得到一个 JSP/Facelets 页面和一个 JavaBean 类,而无需自定义 servlet。


  • 如果您想调用一些 Java 代码来控制JSP 页面内的流程,那么您需要获取一个(现有的)流程控制标签库,如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>
    

    使用非常适合所有 HTML 的 XML 样式标签,与一堆带有各种左大括号和右大括号(“这个右大括号到底属于哪里?”)的 scriptlet 相比,代码可读性更好(因此更易于维护)。一个简单的帮助是将您的 Web 应用程序配置为在仍然使用scriptlet时抛出异常,方法是将以下部分添加到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")


  • 如果您想直接在 JSP 页面中调用一些实用Java 代码(通常是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 无关,在您开发的每个Web 应用程序中需要考虑这个问题。scriptlet的问题在于它没有提供内置的预防方法,至少没有使用标准的 Java API。JSP 的后继者 Facelets 已经隐含了 HTML 转义,所以你不需要担心 Facelets 中的 XSS 漏洞。

See also:

也可以看看:

回答by Bozho

JSTLoffers tags for conditionals, loops, sets, gets, etc. For example:

JSTL为条件、循环、集合、获取等提供标签。例如:

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

JSTL works with request attributes - they are most often set in the request by a Servlet, which forwardsto the JSP.

JSTL 使用请求属性 - 它们通常由 Servlet 在请求中设置,然后转发到 JSP。

回答by tzim

I'm not sure if i get this correct.

我不确定我是否理解正确。

You should read something about MVC. Spring MVC& Struts 2are the two most common solutions.

你应该阅读一些关于 MVC 的内容。Spring MVCStruts 2是两种最常见的解决方案。

回答by Thorbj?rn Ravn Andersen

Experience has shown that JSP's have some shortcomings, one of them being hard to avoid mixing markup with actual code.

经验表明,JSP 有一些缺点,其中之一是难以避免将标记与实际代码混合。

If you can, then consider using a specialized technology for what you need to do. In Java EE 6 there is JSF 2.0, which provides a lot of nice features including gluing Java beans together with JSF pages through the #{bean.method(argument)}approach.

如果可以,请考虑使用专门的技术来完成您需要做的事情。在 Java EE 6 中有 JSF 2.0,它提供了许多不错的特性,包括通过这种#{bean.method(argument)}方法将 Java bean 与 JSF 页面粘合在一起。

回答by Behrang Saeedzadeh

You can use JSTL tags together with EL expressions to avoid intermixing Java and HTML code:

您可以将 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>

回答by Sami Jmii

In the MVC Architectural pattern, JSPs represent the View layer. Embedding java code in JSPs is considered a bad practice. You can use JSTL, freeMarker, velocitywith JSP as "template engine". The data provider to those tags depends on frameworksthat you are dealing with. Struts 2and webworkas an implementation for MVC Pattern uses OGNL"very interesting technique to expose Beans Properties to JSP ".

在 MVC 架构模式中,JSP 代表视图层。在 JSP 中嵌入 Java 代码被认为是一种不好的做法。您可以使用JSTLfreeMarkervelocity和 JSP 作为“模板引擎”。这些标签的数据提供者取决于您正在处理的框架Struts 2并且webwork作为 MVC 模式的实现,使用OGNL“将 Beans 属性公开给 JSP 的非常有趣的技术”。

回答by mahesh

in order to avoid java code in JSP files java now provides tag libraries like JSTL also java has come up with JSF into which u can write all programming structures in the form of tags

为了避免 JSP 文件中的 Java 代码,Java 现在提供了像 JSTL 这样的标签库,而且 Java 还提出了 JSF,您可以将所有编程结构以标签的形式写入其中

回答by tsand

There are also component-based frameworks such as Wicketthat generate a lot of the HTML for you. The tags that end up in the HTML are extremely basic and there is virtually no logic that gets mixed in. The result is almost empty-like HTML pages with typical HTML elements. The downside is that there are a lot of components in the WicketAPI to learn and some things can be difficult to achieve under those constraints.

还有一些基于组件的框架,例如Wicket,可以为您生成大量 HTML。最终出现在 HTML 中的标签非常基本,几乎没有混入任何逻辑。结果几乎是空的 HTML 页面,带有典型的 HTML 元素。缺点是WicketAPI中有很多组件需要学习,在这些限制下有些事情可能很难实现。

回答by msj121

Wicket is also an alternative which completely separates java from html, so a designer and programmer can work together and on different sets of code with little understanding of each other.

Wicket 也是一种将 java 与 html 完全分离的替代方案,因此设计人员和程序员可以一起工作,处理不同的代码集,而彼此几乎不了解。

Look at Wicket.

看看威克特。

回答by Chandra Sekhar

Use JSTL Tag librariesin JSP, that will work perfect.

JSTL Tag libraries在 JSP 中使用,将完美运行。