如何使用JSP 2避免JSP文件中的Java代码?
我是Java EE的新手,我知道类似以下三行内容
I'm new to Java EE and I know that something like the following three lines
<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>
是一种古老的编码方式,在JSP版本2中,存在一种避免在JSP文件中使用Java代码的方法.什么是替代JSP 2行,该技术称为什么?
is an old school way of coding and in JSP version 2 there exists a method to avoid Java code in JSP files. What are the alternative JSP 2 lines, and what is this technique called?
<% %>
事物)的使用自 taglibs 诞生以来,/jsp/info> JSP 的确非常不受欢迎(例如 JSTL )和 EL (
The use of scriptlets (those <% %>
things) in JSP is indeed highly discouraged since the birth of taglibs (like JSTL) and EL (Expression Language, those ${}
things) way back in 2001.
小脚本的主要缺点是:
- 可重用性::您无法重用scriptlet.
- 可替换性:您无法使脚本小脚本抽象化.
- 面向对象功能::您不能使用继承/组成.
- 可调试性::如果scriptlet中途抛出异常,那么您得到的只是空白页.
- 可测试性:脚本无法进行单元测试.
- 可维护性:每维护一次,维护混合/混乱/重复的代码逻辑需要更多的时间.
- Reusability: you can't reuse scriptlets.
- Replaceability: you can't make scriptlets abstract.
- OO-ability: you can't make use of inheritance/composition.
- Debuggability: if scriptlet throws an exception halfway, all you get is a blank page.
- Testability: scriptlets are not unit-testable.
- Maintainability: per saldo more time is needed to maintain mingled/cluttered/duplicated code logic.
Sun Oracle itself also recommends in the JSP coding conventions to avoid use of scriptlets whenever the same functionality is possible by (tag) classes. Here are several cites of relevance:
从JSP 1.2规范开始,强烈建议您在Web应用程序中使用JSP标准标记库(JSTL),以帮助减少页面中对JSP脚本的需求.通常,使用JSTL的页面更易于阅读和维护.
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 scriptlets in your pages. Pages that use JSTL are, in general, easier to read and maintain.
...
在可能的情况下,只要标签库提供等效功能,就避免使用JSP脚本.这使页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分离,并使您的页面更易于演化为JSP 2.0样式的页面(JSP 2.0规范支持但不强调脚本的使用).
Where possible, avoid JSP scriptlets whenever 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).
...
本着采用模型视图控制器(MVC)设计模式来减少表示层与业务逻辑之间的耦合的精神,不应使用JSP脚本来编写业务逻辑.而是,如果有必要,可以使用JSP scriptlet将处理客户端请求所返回的数据(也称为值对象")转换为适当的客户端就绪格式.即使这样,最好还是使用前端控制器servlet或自定义标签来完成.
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 used for 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.
如何替换 scriptlets 完全取决于代码/逻辑的唯一目的.这段代码比以往更常被放置在一个完全有价值的Java类中:
How to replace scriptlets entirely depends on the sole purpose of the code/logic. More than often this code is to be placed in a fullworthy Java class:
-
如果您想在每次请求中调用相同 Java代码,则无论请求的页面如何,都应或多或少地调用它,例如检查用户是否已登录,然后实施过滤器,并相应地在
If you want to invoke the same Java code on every request, less-or-more regardless of the requested page, e.g. checking if a user is logged in, then implement a filter and 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.
}
}
当映射到覆盖感兴趣的JSP页面的适当的<url-pattern>
上时,则无需在整个JSP页面上粘贴相同的代码.
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.
如果您要调用一些Java代码来预处理一个请求,例如从数据库中预加载一些列表以显示在某个表中(如果需要)基于某些查询参数,然后实现 servlet 并编写
If you want to invoke some Java code to preprocess a request, e.g. preloading some list from a database to display in some table, if necessary based on some query parameters, then implement a servlet and 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);
}
}
通过这种方式处理异常更加容易.在JSP呈现过程中,不能访问DB,但是要在显示JSP之前就可以访问DB.每当数据库访问引发异常时,您仍然可以更改响应.在上面的示例中,将显示默认错误500页面,您可以通过web.xml
中的<error-page>
自定义该页面.
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
.
如果您要调用一些Java代码来后处理,例如处理表单提交,然后实现 servlet 并相应地在
If you want to invoke some Java code to postprocess a request, e.g. processing a form submit, then implement a servlet and 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.
如果您要调用一些Java代码来控制,请执行计划和/或请求和响应的目标,然后实施 MVC的前端控制器模式.例如:
If you want to invoke some Java code to control the execution plan and/or the destination of the request and the response, then implement a servlet according 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);
}
}
或者只是采用MVC框架,例如 JSF , Wicket 等,这样一来,您最终只能获得一个JSP/Facelets页面和JavaBean类,而无需自定义servlet.
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.
如果要在JSP页面内调用一些Java代码来控制流,则需要获取一个(现有的)流控制标记库,如
If you want to invoke some Java code to control the flow inside 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>
使用XML样式的标签非常适合所有HTML,与一堆带有各种开合和结束大括号的脚本(")相比,该代码具有更好的可读性(因而更易于维护).属于?" ).一种简单的帮助是将Web应用程序配置为在仍使用 scriptlets 时引发异常,方法是在web.xml
中添加以下内容:
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 scriptlets are 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>
在 Facelets 中,JSP的继任者JSP是Java EE提供的MVC框架的一部分JSF ,已经不能使用小脚本.这样,您会自动被迫以正确的方式"做事.
In Facelets, the successor of JSP, which is part of the Java EE provided MVC framework JSF, it is already not possible to use scriptlets. This way you're automatically forced to do things "the right way".
如果您要调用一些Java代码来在JSP页面中访问并显示后端"数据,则需要使用EL(表达式语言),这些${}
内容.例如.重新显示提交的输入值:
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}" />
${param.foo}
显示request.getParameter("foo")
的结果.
如果要直接在JSP页面中调用某些 utility Java代码(通常是public static
方法),则需要将它们定义为EL函数.有一个标准的功能taglib 在JSTL中,但是您还可以轻松地自己创建函数 .这是一个示例JSTL fn:escapeXml
如何用于防止 XSS
If you want to invoke some utility Java code directly in the JSP page (typically public static
methods), then you need to define them as EL functions. There's a standard functions taglib in JSTL, but you can also easily create functions yourself. Here's an example how JSTL fn:escapeXml
is useful to prevent XSS attacks.
<%@ 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应用程序中都应考虑到此问题. scriptlets 的问题在于,它没有提供内置的预防措施,至少没有使用标准的Java API. JSP的继任者Facelets已经具有隐式HTML转义,因此您不必担心Facelets中的XSS漏洞.
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 every web application you develop. The problem of scriptlets is 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.
- JSP与Servlet有什么区别和JSF?
- Servlet,ServletContext,HttpSession和HttpServletRequest/Response如何工作?
- 带有JSP,Servlet和JDBC的基本MVC示例
- Java Web应用程序中的设计模式
- JSP/Servlet的隐藏功能
- What's the difference between JSP, Servlet and JSF?
- How does Servlet, ServletContext, HttpSession and HttpServletRequest/Response work?
- Basic MVC example with JSP, Servlet and JDBC
- Design patterns in Java web applications
- Hidden features of JSP/Servlet