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

3df52oht  于 2022-09-16  发布在  Java
关注(0)|答案(30)|浏览(255)

我是Java EE的新手,我知道如下三行

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

是一种老式的编码方式,在JSP版本2中,有一种方法可以避免JSP文件中的Java代码。什么是可选的JSP2行,这种技术叫什么?

xoshrz7s

xoshrz7s16#

JSP中使用Scriptlet(那些<% %>的东西)确实是非常不鼓励的,因为早在2001年就出现了taglibs(像JSTL)和ELExpression Language,那些${}的事情)。

  • scriptlet*的主要缺点是:

1.*可重用性:*不能重用脚本。
1.*可替换性:*您不能将scriptlet抽象。
1.*OO能力:*您不能使用继承/组合。
1.*可调试性:*如果scriptlet中途抛出异常,则得到的只是一个空白页。
1.*可测试性:*脚本不可单元测试。
1.可维护性:每个saldo需要更多的时间来维护混合/混乱/重复的代码逻辑。
Sun Oracle本身也在JSP coding conventions中建议,只要(标记)类可以实现相同的功能,就避免使用
Scriptlet
。以下是几个相关的例子:
根据JSP 1.2规范,强烈建议在web应用程序中使用JSP标准标记库(JSTL),以帮助减少页面中对JSP脚本的需求。通常,使用JSTL的页面更易于阅读和维护。
...
在可能的情况下,避免使用JSP脚本,只要标记库提供相同的功能。这使页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分离,并使您的页面更容易演变为JSP2.0样式的页面(JSP2.1规范支持但不强调使用脚本)。
...
本着采用模型-视图-控制器(MVC)设计模式减少表示层与业务逻辑之间耦合的精神,JSP脚本不应用于编写业务逻辑。相反,如果需要,可以使用JSP脚本将处理客户机请求返回的数据(也称为“值对象”)转换为适合客户机的格式。即使如此,最好使用前端控制器servlet或自定义标记。

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

  • 如果您希望在每个请求上调用相同的Java代码,无论请求的页面是多少,例如检查用户是否登录,则实现filter并在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.
      }
  }

当Map到覆盖感兴趣的JSP页面的适当<url-pattern>时,您不需要复制粘贴整个JSP页面的相同代码。

  • 如果您想调用一些Java代码处理GET请求,例如,根据一些查询参数,从数据库中预加载一些列表以显示在某些表中,则实现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呈现过程中访问,但远远早于JSP显示。您仍然可以在DB访问抛出异常时更改响应。在上述示例中,将显示默认错误500页面,您可以通过web.xml中的<error-page>进行自定义。

  • 如果您想调用一些Java代码处理POST请求,例如从提交的HTML表单中收集数据并对其进行一些业务处理(转换、验证、保存在数据库中等),则实现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.
      }
  }

这种处理不同结果页目的地的方法更容易:在出现错误时重新显示带有验证错误的表单(在这个特定示例中,您可以使用EL中的${message}重新显示表单),或者在成功的情况下直接转到所需的目标页。

  • 如果您想调用一些Java代码来控制执行计划和/或请求和响应的目的地,那么根据MVC的前端控制器模式实现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);
      }
  }

或者只采用MVC框架,如JSFSpring MVC、D1C2C1D等,这样您最终只需要一个JSP/Facelets页面和一个JavaBean类,而不需要定制servlet。

  • 如果您想调用一些Java代码来控制JSP页面内的流,那么您需要获取(现有的)流控制标记库,如JSTL core。E、 g.在表格中显示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,将web应用程序配置为在仍然使用Scriptlet时抛出异常:

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

在作为Java EE提供的MVC框架JSF的一部分的JSP的继承者Facelets中,已经不可能使用Scriptlet。这样,你会自动地被迫以“正确的方式”做事。

  • 如果您想调用一些Java代码访问并显示JSP页面中的“后端”数据,那么您需要使用EL(表达式语言),即${}。E、 g.重新显示提交的输入值:
<input type="text" name="foo" value="${param.foo}" />

${param.foo}显示request.getParameter("foo")的结果。

<%@ 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/whatever没有任何具体关系,您开发的每个web应用程序都需要考虑这个问题。scriptlets的问题在于,它没有提供内置的预防方法,至少没有使用标准Java API。JSP的后续Facelets已经具有隐式HTML转义,因此您不需要担心Facelets中的XSS漏洞。

另见:

  • JSP、Servlet和JSF之间有什么区别?
  • Servlet、ServletContext、HttpSession和HttpServletRequest/Respon如何工作
ohtdti5x

ohtdti5x17#

只需使用JSTL标记和EL表达式。

bxfogqkk

bxfogqkk18#

在JSP中使用JSTL标记库。这将非常有效。

vawmfj5a

vawmfj5a19#

无论您如何努力避免,当您与其他开发人员合作时,他们中的一些人仍然会更喜欢scriptlet,然后将邪恶的代码插入到项目中。因此,如果您真的想减少scriptlet代码,那么在第一次登录时设置项目非常重要。有几种技术可以解决这个问题(包括其他提到的几种框架)。但是,如果您更喜欢纯JSP方式,则使用JSTL标记文件。这样做的好处是,您还可以为您的项目设置母版页,以便其他页面可以继承母版页

创建名为base的母版页。在WEB-INF/标记下标记以下内容:

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>

<%@attribute name="title" fragment="true" %>

<html>
  <head>
    <title>  
       <jsp:invoke fragment="title"></jsp:invoke>
    </title>

  </head>
  <body>
    <div id="page-header">
       ....
    </div>
    <div id="page-body">
      <jsp:doBody/>
    </div>
    <div id="page-footer">
      .....
    </div>
  </body>
</html>

在这个母版页面上,我创建了一个名为“title”的片段,以便在子页面中,我可以在母版页面的这个位置插入更多代码。此外,标签<jsp:doBody/>将被子页面的内容替换

在WebContent文件夹中创建子页面(child.jsp):

<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:base>
    <jsp:attribute name="title"> 
        <bean:message key="hello.world" />
    </jsp:attribute>

    <jsp:body>
    [Put your content of the child here]
    </jsp:body>   
</t:base>

<t:base>用于指定要使用的母版页(此时为base.tag)。此处标记<jsp:body>内的所有内容将替换母版页上的<jsp:doBody/>。您的子页面还可以包括任何标记库,您可以像前面提到的那样正常使用它。但是,如果您在此处使用任何scriptlet代码(<%= request.getParameter("name") %>…),并尝试运行此页面,您将获得JasperException because Scripting elements ( &lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) are disallowed here。因此,其他人无法将恶意代码包含到jsp文件中

从控制器调用此页面:

你可以很容易地给孩子打电话。来自控制器的jsp文件。这也适用于struts框架

sgtfey8w

sgtfey8w20#

为了避免JSP文件中的Java代码,Java现在提供了标记库,如JSTL。
此外,Java提出了JSF,您可以将所有编程结构以标记的形式写入其中。

izkcnapc

izkcnapc21#

您提出了一个很好的问题,尽管您得到了很好的答案,但我建议您摆脱JSP。这是过时的技术,最终会消亡。使用现代方法,如模板引擎。您将有非常清晰的业务层和表示层分离,而且模板中肯定没有Java代码,因此您可以直接从web表示编辑软件生成模板,在大多数情况下利用所见即所得。
当然,请远离过滤器和前后处理,否则您可能会遇到支持/调试困难,因为您始终不知道变量从何处获得值。

zqry0prt

zqry0prt22#

Wicket也是一种将Java与HTML完全分离的替代方案,因此设计师和程序员可以一起工作并处理不同的代码集,而彼此几乎不了解。
看那个门。

wfsdck30

wfsdck3023#

学习使用JSTL自定义和编写自己的标记

请注意,EL是邪恶(运行时异常和重构)。
Wicket也可能是邪恶的(对于小型应用程序或简单视图层,性能和麻烦)。

来自java2s的示例

这必须添加到web应用程序的web.xml中

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

创建文件java2s。/WEB-INF中的tld*/*

<!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>
6uxekuva

6uxekuva24#

如果您只是想避免JSP中Java编码的缺点,那么即使使用scriplets也可以这样做。只需遵循一些规则,在JSP中使用最少的Java,在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 forwarding, 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>
8ljdwjyq

8ljdwjyq25#

经验表明,JSP有一些缺点,其中之一是难以避免将标记与实际代码混合。
如果可以,那么考虑使用专门的技术来完成您需要做的事情。在JavaEE6中有JSF2.0,它提供了许多很好的特性,包括通过#{bean.method(argument)}方法将JavaBean与JSF页面粘合在一起。

m3eecexj

m3eecexj26#

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

cclgggtu

cclgggtu27#

还有一些基于组件的框架,如Wicket,可以为您生成大量HTML。
在HTML中结束的标记是非常基本的,实际上没有任何逻辑可以混入其中。结果几乎是空的,就像具有典型HTML元素的HTML页面一样。缺点是WicketAPI中有很多组件需要学习,在这些约束条件下,有些东西很难实现。

ztyzrc3y

ztyzrc3y28#

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

xbp102n029#

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

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

JSTL与请求属性一起工作——它们通常由Servlet在请求中设置,Servlet将转发到JSP。

1yjd4xko

1yjd4xko30#

作为保护:永久禁用脚本

正如another question所讨论的,您可以并且应该始终禁用web.xml web应用程序描述符中的脚本。
我总是这样做,以防止任何开发人员添加scriptlet,尤其是在大公司中,您迟早会失去概览。web.xml设置如下所示:

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

相关问题