Skip to content

Commit d7041c2

Browse files
committed
新增"如何避免在JSP文件中使用Java代码"
1 parent f756ffa commit d7041c2

File tree

3 files changed

+175
-1
lines changed

3 files changed

+175
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ stackoverflow-Java-top-qa
4646
* [Java中打印一个数组最简单的方法是什么](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/What's-the-simplest-way-to-print-a-Java-array.md)
4747
* [为什么以下用随机生成的文字会得出 “hello world”?](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/why-does-this-code-using-random-strings-print-hello-world.md)
4848
* [什么在java中存放密码更倾向于char[]而不是String](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/why-is-cha[]-preferred-over-String-for-passwords-in-java.md)
49+
* [如何避免在JSP文件中使用Java代码](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/how-to-avoid-java-code-in-jsp-files.md)
4950

5051
> 网络
5152
@@ -72,7 +73,6 @@ stackoverflow-Java-top-qa
7273
- [Creating a memory leak with Java [closed]](http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java)
7374
- [Why is printing “B” dramatically slower than printing “#”?](http://stackoverflow.com/questions/21947452/why-is-printing-b-dramatically-slower-than-printing)
7475
- [How can I create an executable jar with dependencies using Maven?](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven)
75-
- [How to avoid Java code in JSP files?](http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files)
7676
- [Why is executing Java code in comments with certain Unicode characters allowed?](http://stackoverflow.com/questions/30727515/why-is-executing-java-code-in-comments-with-certain-unicode-characters-allowed)
7777
- [Dealing with “java.lang.OutOfMemoryError: PermGen space” error](http://stackoverflow.com/questions/88235/dealing-with-java-lang-outofmemoryerror-permgen-space-error)
7878
- [“implements Runnable” vs. “extends Thread”](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
##如何避免在JSP文件中使用Java代码
2+
3+
###问题
4+
如何避免在JSP文件中使用Java代码?
5+
6+
我对Java EE不是很熟悉,我知道类似如下的三行代码
7+
```jsp
8+
<%= x+1 %>
9+
<%= request.getParameter("name") %>
10+
<%! counter++; %>
11+
```
12+
这三行代码是学校教的老式代码。在JSP 2,存在一些方法可以避免在JSP文件中使用Java代码。有人可以告诉我在JSP 2中如何避免使用Java代码吗,这些方法该如何使用?
13+
14+
###回答
15+
在大约十年前,taglibs(比如JSTL)和EL(EL表达式,`${}`)诞生的时候,在JSP中使用scriptlets(类似`<% %>`)这种做法,就确实已经是不被鼓励使用的做法了。
16+
17+
scriptlets 主要的缺点有:
18+
1. **重用性** :你不可以重用scriptlets
19+
2. **可替换性** :你不可以让scriptlets抽象化
20+
3. **面向对象能力** :你不可以使用继承或组合
21+
4. **调试性** :如果scriptlets中途抛出了异常,你只能获得一个空白页
22+
5. **可测试性** :scriptlets不能进行单元测试
23+
6. **可维护性** :(这句有些词语不确定)需要更多的时间去维护混合的/杂乱的/冲突的 代码逻辑
24+
25+
Oracle自己也在 [JSP coding conventions](http://www.oracle.com/technetwork/articles/javase/code-convention-138726.html)一文中推荐在功能可以被标签库所替代的时候避免使用scriptlets语法。以下引用它提出的几个观点:
26+
27+
> 在JSP 1.2规范中,强烈推荐使用JSTL来减少JSP scriptlets语法的使用。一个使用JSTL的页面,总得来说会更加地容易阅读和维护。
28+
29+
>...
30+
31+
>在任何可能的地方,当标签库能够提供相同的功能时,尽量避免使用JSP scriptlets语法。这会让页面更加容易阅读和维护,帮助将 业务逻辑 从 表现层逻辑 中分离,也会让页面往更符合JSP 2.0风格的方向发展(JSP 2.0规范中,支持但是极大弱化了JSP scriptlets语法)
32+
33+
>...
34+
35+
>本着适应 模型-显示层-控制器(MVC) 设计模式中关于减少业务逻辑层与显示层之间的耦合的精神,**JSP scriptlets语法不应该**被用来编写业务逻辑。相应的,JSP scriptlets语法在传送一些服务端返回的处理客户端请求的数据(也叫做 声明对象?。这里翻译得不清楚)的时候会被使用。尽管如此,使用一个controller servlet来处理或者用自定义标签来处理会更好。
36+
37+
-----------
38+
39+
**如何替换scriptlets语句,取决于代码/逻辑的目的。更常见的是,被替换的语句会被放在另外的一些更值得放的Java类里**(这里翻译得不一定清楚)
40+
41+
* 如果你想在每个请求运行**相同的**Java代码,less-or-more regardless of the requested page(不会翻译),比如说 检查一个用户是否在登录状态,就要实现一个 过滤器,在doFilter()方法中编写正确的代码,例如
42+
43+
```java
44+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
45+
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
46+
((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page.
47+
} else {
48+
chain.doFilter(request, response); // Logged in, just continue request.
49+
}
50+
}
51+
```
52+
当你在`<url-pattern>`中做好恰当的地址映射,覆盖所有应该被覆盖的JSP文件,也就不需要再JSP文件中添加这些相同的Java代码
53+
54+
----------------
55+
56+
* 如果你想执行一些Java代码来**预处理**一个请求,例如,预加载某些从数据库加载的数据来显示在一些表格里,可能还会有一些查询参数,那么,实现一个Servlet,在doGet()方法里编写正确的代码,例如
57+
58+
```java
59+
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
60+
try {
61+
List<Product> products = productService.list(); // Obtain all products.
62+
request.setAttribute("products", products); // Store products in request scope.
63+
request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table.
64+
} catch (SQLException e) {
65+
throw new ServletException("Retrieving products failed!", e);
66+
}
67+
}
68+
```
69+
这个方法能够更方便地处理异常。The DB is not accessed in the midst of JSP rendering, but far before the JSP is been displayed.(不会翻译)在数据库抛出异常的时候,你还是有改变response的可能。在上面的例子,默认的500页会显示,你还可以改变`web.xml``<error-page>`来自定义异常处理错误页。
70+
71+
----------
72+
73+
* 如果你想执行一些Java代码来**后置处理(postprocess)**一个请求,例如处理表单提交,那么,实现一个Servlet,在doPost()里写上正确的代码:
74+
75+
```java
76+
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
77+
String username = request.getParameter("username");
78+
String password = request.getParameter("password");
79+
User user = userService.find(username, password);
80+
81+
if (user != null) {
82+
request.getSession().setAttribute("user", user); // Login user.
83+
response.sendRedirect("home"); // Redirect to home page.
84+
} else {
85+
request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope.
86+
request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error.
87+
}
88+
}
89+
```
90+
这个处理不同目标结果页的方法会比原来更加简单: 可以显示一个带有表单验证错误提示的表单(在这个特别的例子中,你可以用EL表达式`${message}`来显示错误提示),或者仅仅跳转到成功的页面
91+
92+
---------
93+
94+
* 如果你想执行一些Java代码来**控制**执行计划(control the execution plan) 和/或 request和response的目标,用[MVC模式](http://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/3542297#3542297)实现一个Servlet,例如:
95+
```java
96+
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
97+
try {
98+
Action action = ActionFactory.getAction(request);
99+
String view = action.execute(request, response);
100+
101+
if (view.equals(request.getPathInfo().substring(1)) {
102+
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
103+
} else {
104+
response.sendRedirect(view);
105+
}
106+
} catch (Exception e) {
107+
throw new ServletException("Executing action failed.", e);
108+
}
109+
}
110+
```
111+
或者使用一些MVC框架例如[JSF](http://stackoverflow.com/tags/jsf/info), [Spring MVC](http://stackoverflow.com/tags/spring-mvc/info), [Wicket](http://stackoverflow.com/tags/wicket/info) end up with just a JSP/Facelets page and a Javabean class without the need for a custom servlet.(不知道如何翻译准确)
112+
113+
---------
114+
115+
* 如果你想执行一些Java代码来**控制JSP页面的执行流程(control the flow inside a JSP page)**,那么你需要使用一些(已经存在的)流程控制标签库,比如[JSTL core](http://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/c/tld-summary.html),例如,在一个表格显示`List<Product>`
116+
117+
```java
118+
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
119+
...
120+
<table>
121+
<c:forEach items="${products}" var="product">
122+
<tr>
123+
<td>${product.name}</td>
124+
<td>${product.description}</td>
125+
<td>${product.price}</td>
126+
</tr>
127+
</c:forEach>
128+
</table>
129+
```
130+
这些XML风格的标签可以很好地适应HTML代码,代码变得更好阅读(也因此更好地维护),相比于杂乱无章的scriptlets 的分支大括号(Where the heck does this closing brace belong to?"(到底这个结束大括号是属于哪个代码段的?))。一个简单的设置可以配置你的Web程序让在使用scriptlets 的时候自动抛出异常
131+
132+
```xml
133+
<jsp-config>
134+
<jsp-property-group>
135+
<url-pattern>*.jsp</url-pattern>
136+
<scripting-invalid>true</scripting-invalid>
137+
</jsp-property-group>
138+
</jsp-config>
139+
```
140+
141+
在JSP的继承者[Facelets](http://stackoverflow.com/tags/facelets/info)里(Java EE提供的MVC框架[JSF](http://stackoverflow.com/tags/jsf/info)),已经**不**可能使用scriptlets语法了。这是一个让你强制使用“正确的方法”的方法
142+
143+
-----------
144+
145+
* 如果你想执行一些Java代码来在JSP中 **访问和显示** 一些“后端”数据,你需要使用EL(表达式),`${}`,例如,显示已经提交了的数值:
146+
147+
```jsp
148+
<input type="text" name="foo" value="${param.foo}" />
149+
```
150+
151+
`${param.foo}`会显示`request.getParameter("foo")`这句话的输出结果。
152+
153+
--------------
154+
155+
* 如果你想在JSP直接执行一些工具类Java代码(典型的,一些public static方法),你需要定义它,并使用EL表达式函数。这是JSTL里的标准函数标签库,但是你也可以[轻松地创建自己需要的功能](http://docs.oracle.com/javaee/5/tutorial/doc/bnahq.html#bnaiq),下面是一个使用有用的`fn:escapeXml`来避免XSS攻击的例子。
156+
157+
```java
158+
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
159+
...
160+
<input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
161+
```
162+
163+
注意,XSS并不是Java/JSP/JSTL/EL/任何技术相关的东西,这个问题是**任何**Web应用程序都需要关心的问题,scriptlets 并没有为这个问题提供良好的解决方案,至少没有标准的Java API的解决方案。JSP的继承者Facelets内含了HTML转义功能,所以在Facelets里你不用担心XSS攻击的问题。
164+
165+
See Also:
166+
* [JSP, Servlet, JSF的不同点在哪里?](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp/2097732#2097732)
167+
* [Servlet, ServletContext, HttpSessionHttpServletRequest/Response 是如何工作的?](http://stackoverflow.com/questions/3106452/java-servlet-instantiation-and-session-variables/3106909#3106909)
168+
* [JSP, Servlet and JDBC的基本MVC例子](http://stackoverflow.com/questions/5003142/jsp-using-mvc-and-jdbc)
169+
* [Java Web应用程序中的设计模式](http://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/)
170+
* [JSP/Servlet中的隐藏功能](http://balusc.blogspot.com/2010/01/hidden-features-of-jspservlet.html)
171+
172+
173+
stackoverflow原址:http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files

contents/why-is-cha[]-preferred-over-String-for-passwords-in-java.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ String是不可变的。虽然String加载密码之后可以把这个变量扔
1515

1616
**stackoverflow链接**
1717
http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords-in-java
18+
1819
**知乎上也有相关讨论**
1920
https://www.zhihu.com/question/36734157

0 commit comments

Comments
 (0)