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

2024-03-03

我知道类似以下三行

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

是一种老式的编码方式,在 JSP 版本 2 中存在一种避免 JSP 文件中包含 Java 代码的方法。有哪些替代的 JSP 2 线路,这种技术叫什么?


指某东西的用途小脚本 (those <% %>的东西)在JSP https://stackoverflow.com/tags/jsp/info确实从诞生之日起就备受劝阻taglibs (like JSTL https://stackoverflow.com/tags/jstl/info) and EL https://stackoverflow.com/tags/el/info (表达语言 https://stackoverflow.com/tags/el/info, those ${}事情)早在2001年。

主要缺点小脚本 are:

  1. 可重复使用性:您不能重复使用 scriptlet。
  2. 可更换性:你不能让 scriptlet 变得抽象。
  3. OO能力:你不能利用继承/组合。
  4. 可调试性:如果 scriptlet 中途抛出异常,您得到的只是一个空白页。
  5. 可测试性:scriptlet 不可进行单元测试。
  6. 可维护性:根据萨尔多的说法,需要更多时间来维护混合/混乱/重复的代码逻辑。

Sun Oracle 本身也推荐在JSP 编码约定 https://www.oracle.com/technical-resources/articles/javase/code-convention.html以避免使用小脚本只要(标记)类可以实现相同的功能。以下是一些相关的引用:

从 JSP 1.2 规范开始,强烈建议在 Web 应用程序中使用 JSP 标准标记库 (JSTL),以帮助减少对 JSP scriptlet 的需求在您的页面中。一般来说,使用 JSTL 的页面更易于阅读和维护。

...

在可能的情况,避免 JSP 小脚本每当标签库提供同等功能时。这使得页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分开,并使您的页面更容易发展为 JSP 2.0 样式的页面(JSP 2.0 规范支持但不再强调 scriptlet 的使用)。

...

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


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

  • 如果你想调用sameJava 代码上every请求,或多或少,无论请求的页面如何,例如检查用户是否登录,然后实施filter https://stackoverflow.com/tags/servlet-filters/info并相应地编写代码doFilter() https://jakarta.ee/specifications/platform/9/apidocs/jakarta/servlet/filter#doFilter-jakarta.servlet.ServletRequest-jakarta.servlet.ServletResponse-jakarta.servlet.FilterChain-方法。例如。:

      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 请求,例如从数据库预加载一些列表以显示在某些表中,如果需要的话基于一些查询参数,然后实现servlet https://stackoverflow.com/tags/servlets/info并相应地编写代码doGet() https://jakarta.ee/specifications/platform/9/apidocs/jakarta/servlet/http/httpservlet#doGet-jakarta.servlet.http.HttpServletRequest-jakarta.servlet.http.HttpServletResponse-方法。例如。:

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

    这种方式处理异常更加容易。 DB 不是在 JSP 渲染过程中访问的,而是在 JSP 显示之前访问的。每当数据库访问引发异常时,您仍然可以更改响应。在上面的示例中,将显示默认的错误 500 页面,您可以通过自定义<error-page> in web.xml.


  • 如果你想调用一些Java代码处理 POST 请求,例如从提交的 HTML 表单中收集数据并用它做一些业务(转换、验证、保存在数据库中等),然后实现一个servlet https://stackoverflow.com/tags/servlets/info并相应地编写代码doPost() https://jakarta.ee/specifications/platform/9/apidocs/jakarta/servlet/http/httpservlet#doPost-jakarta.servlet.http.HttpServletRequest-jakarta.servlet.http.HttpServletResponse-方法。例如。:

      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 https://stackoverflow.com/tags/el/info),或者在成功的情况下直接进入所需的目标页面。


  • 如果你想调用一些Java代码control执行计划和/或请求和响应的目的地,然后实施servlet https://stackoverflow.com/tags/servlets/info根据MVC的前端控制器模式 https://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/3542297#3542297. 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);
          }
      }
    

    或者只是采用 MVC 框架,例如JSF https://stackoverflow.com/tags/jsf/info, 春季MVC https://stackoverflow.com/tags/spring-mvc/info, Wicket https://stackoverflow.com/tags/wicket/info等,这样您最终只需一个 JSP/Facelets 页面和一个 JavaBean 类,而不需要自定义 servlet。


  • 如果你想调用一些Java代码控制流量在 JSP 页面内,那么您需要获取一个(现有的)流程控制标签库,例如JSTL核心 https://jakarta.ee/specifications/tags/1.2/tagdocs/c/tld-summary.html。例如。显示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 应用程序配置为在任何时候抛出异常小脚本通过添加以下片段仍然可以使用web.xml:

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

    In Facelets https://stackoverflow.com/tags/facelets/info,JSP的后继者,它是Java EE提供的MVC框架的一部分JSF https://stackoverflow.com/tags/jsf/info,已经是not可以使用小脚本。这样你就会自动被迫以“正确的方式”做事。


  • 如果你想调用一些Java代码访问和显示JSP页面内部的“后端”数据,那么你需要使用EL(表达式语言),那些${}事物。例如。重新显示提交的输入值:

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

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


  • 如果你想调用一些utilityJava代码直接在JSP页面中(通常是public static方法),那么您需要将它们定义为 EL 函数。有一个标准函数标签库 https://jakarta.ee/specifications/tags/1.2/tagdocs/fn/tld-summary.html在 JSTL 中,但是您也可以轻松地自己创建函数 https://stackoverflow.com/questions/6395621/how-to-call-a-static-method-in-jsp-el。下面是 JSTL 的示例fn:escapeXml有利于防止XSS攻击 https://en.wikipedia.org/wiki/Cross-site_scripting.

      <%@ 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/任何其他内容没有任何具体关系,这个问题需要在every您开发的网络应用程序。的问题小脚本最大的问题是它没有提供内置的预防措施,至少没有使用标准的 Java API。 JSP 的后继者 Facelets 已经具有隐式 HTML 转义功能,因此您无需担心 Facelets 中的 XSS 漏洞。

也可以看看:

  • JSP、Servlet 和 JSF 有什么区别? https://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp/2097732#2097732
  • Servlet、ServletContext、HttpSession 和 HttpServletRequest/Response 如何工作? https://stackoverflow.com/questions/3106452/java-servlet-instantiation-and-session-variables/3106909#3106909
  • 使用 JSP、Servlet 和 JDBC 的基本 MVC 示例 https://stackoverflow.com/questions/5003142/jsp-using-mvc-and-jdbc
  • Java Web 应用程序中的设计模式 https://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/
  • JSP/Servlet的隐藏特性 https://balusc.omnifaces.org/2010/01/hidden-features-of-jspservlet.html
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 JSP 2 避免 JSP 文件中的 Java 代码? 的相关文章

  • 如何用Java写入OS系统日志?

    Mac OS 有一个名为 Console 的应用程序 其中包含记录的消息 错误和故障 我相信 Windows 中的等效项是事件查看器 我想 Linux 上也有一个 但我不知道它是什么 也不知道它在哪里 是否可以像这样从 Java 输出获取消
  • 指纹奇异点检测

    我正在尝试确定指纹的核心点和增量点 我正在使用庞加莱指数方法 但我无法成功检测到这一点 而且我不明白为什么 First I divide the image in 15x15 blocks then I calculate the x an
  • 重构——套接字中的良好实践——简单的服务器-客户端 Swing 应用程序

    我使用单例和观察者模式编写了一个带有 Swing 接口的简单服务器 客户端程序 每个客户端都连接到服务器并可以发送消息 服务器将其收到的消息转发给其余的客户端 客户端使用 GUI 允许它们随时连接和断开与服务器的连接 该程序运行得很好 因为
  • 如何使用 Java 创建多个模式连接?

    我必须使用两个数据库 DB2 Oracle 我在 DB2 数据库中有一个名为NAVID 我想使用 Java 为 Oracle 中的所有表创建相同的架构 public class automateExport static String va
  • 如何防止在 CXF Web 服务客户端中生成 JAXBElement

    我正在尝试使用 CXF 创建一个 Web 服务客户端来使用 WCF Web 服务 当我使用 wsdl2java 时 它生成具有 JAXBElement 类型而不是 String 的对象 我读到有关使用 jaxb bindings xml 文
  • 在 Tomcat 上部署 Java Web 项目,无需 WAR 或 EAR

    我有一个 Java Web 项目 Struts Spring 在我的本地主机上完美运行 我必须将其部署在我的网站上 但虚拟主机提供的 Tomcat Manager 界面显示 由于安全原因 它无法上传 WAR 文件 当联系技术支持时 我被告知
  • 如何在Mac上使用eclipse安装jetty

    我是一个新手 jetty 和 RESTful API 我想使用 Jetty 创建 REST 服务 并希望将嵌入式 jetty 与 eclipse 一起使用 任何人都可以建议我在 Mac OS 中使用 Eclipse 安装 Jetty Jet
  • org.postgresql.util.PSQLException:协议错误。会话设置失败

    我知道这些类型的问题已经存在 但提供的解决方案对我不起作用 在我的应用程序中 没有版本不匹配的黑白驱动程序和 PostgreSQL 服务器 我还没有找到任何其他解决方案 我正在使用 PostgreSQL 服务器 9 4 和 postgres
  • 如果按下 Esc 则中断循环

    我用 JAVA 语言编写了一个程序 它使用 Scanner 类接受来自控制台的输入 现在我想将此功能添加到我的代码中 以便在用户按下 Esc 按钮时存在循环 while 到目前为止 我认为键盘类可以帮助我 但它就像扫描仪一样 我尝试使用事件
  • 动画图像视图

    目前我正在开发一款游戏 这是我的游戏的详细信息 用户应选择正确的图像对象 我希望图像从左到右加速 当他们到达终点时 他们应该再次出现在活动中 这是我正在处理的屏幕截图 我有 5 个图像视图 它们应该会加速 您有此类动画的示例代码吗 非常感谢
  • Scala(或 Java)中泛型函数的特化

    是否可以在 Scala 中专门化泛型函数 或类 例如 我想编写一个将数据写入 ByteBuffer 的通用函数 def writeData T buffer ByteBuffer data T buffer put data 但由于 put
  • MessageDigest MD5 算法未返回我期望的结果

    我脑后的某个东西告诉我 我在这里遗漏了一些明显的东西 我正在将现有的 java 项目与第三方 api 集成 该第三方 api 使用 api 密钥的 md5 哈希进行身份验证 它对我不起作用 在调试过程中我意识到我生成的哈希值与他们提供的示例
  • Kerberos 缓存票证

    我使用的是 Windows 7 64 位 我创建了一个简单的应用程序来对实现 PrivilegedAction 的类的 run 方法中的文件进行计数 以下是我的 jaas conf 文件 CountFiles com sun securit
  • 如何在 Java 中创建要打印到 JFrame 的 JLabels 数组

    我正在尝试制作一系列标签 每个标签都有一个来自函数的不同值 我不知道要使用的标签的确切数量 我的意思是可以打印任意数量的值 请帮我做这件事 很简单 只需一个方法返回一个数组或一些 JLabels 集合 并将它们全部添加到您的 JCompon
  • setKeyListener 将覆盖 setInputType 并更改键盘

    大家好 我在两个设备之间遇到问题 在实践中使用InputType和KeyListener我正在操纵一个EditText让它从数字键盘接收逗号和数字 有关更多背景信息 请检查我之前的question https stackoverflow c
  • Java 8根据Map属性过滤Map对象列表以删除一些重复项

    Have a List
  • 设计抽象类时是否应该考虑序列化问题?

    一般来说这个问题来自Eclipse建议在抽象类上添加串行版本UID 由于该类是抽象类 因此该类的实例永远不会存在 因此它们永远不会被序列化 只有派生类才会被序列化 所以我的问题是放置一个安全 SuppressWarnings serial
  • BoneCP 和 Derby - 如何正确关闭

    I have BoneCP CONNECTION POOL CONNECTION POOL getConfig setJdbcUrl jdbc derby database shutdown true Connection connecti
  • 条件查询:按计数排序

    我正在尝试执行一个标准查询 该查询返回 stackoverflow 中回答最多的问题 例如常见问题解答 一个问题包含多个答案 我正在尝试使用标准查询返回按每个问题的答案数排序的回答最多的问题 任何人都知道我应该在 hibernate cri
  • 在多线程环境中,Collections.sort 方法有时会抛出 ConcurrentModificationException。列表没有进行结构性修改

    package CollectionsTS import java util ArrayList import java util Collections import java util HashSet import java util

随机推荐