【问题标题】:JSP EL and scope attribute confusionJSP EL 和范围属性混淆
【发布时间】:2011-04-01 00:16:34
【问题描述】:

我想请您帮忙澄清一些问题。但是,首先,一些代码是入站的——这是我构建的一个非常简单的登录示例。

容器是 Tomcat 5.5.27。

假设输入了正确的用户名和密码组合;问题在底部。

LoginPage.jsp(入口点 - 视图)

<%@ page language="java" contentType="text/html; charset=windows-1250" pageEncoding="windows-1250"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <link rel="stylesheet" type="text/css" href="mystyle.css" />
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  <title>Login Page</title>
 </head>
 <body>
  <div id="page">
   <div id="content_container">
    <div id="content">
     <form action="LoginServlet">
     Username: <input type="text" name="username"><br>
     Password: <input type="text" name="password"><br>
     <input type="submit" value="Submit">
     </form>
    </div>
   </div>
  </div>
 </body>
</html>

LoginServlet.java(控制器)

public class LoginServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;

public LoginServlet() {
  super();
}

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  try {
   UserBean user = new UserBean();
   user.setUsername(request.getParameter("username"));
   user.setPassword(request.getParameter("password"));

   user = UserDAO.login(user);

   if(user.isValid()){
    HttpSession session = request.getSession();
    session.setAttribute("currentSessionUser", user);
    response.sendRedirect("userLogged.jsp");
   } else {
    response.sendRedirect("invalidLogin.jsp");
   }
  } catch (Exception e){
   e.printStackTrace();
  }
 }
}

UserDAO.java(“服务”类)

//snipped imports and such

public class UserDAO {

    static Connection currConn = null;
    static ResultSet rs = null;

    public static UserBean login(UserBean userBean) {
        Statement stmt = null;

        String username = userBean.getUsername();
        String password = userBean.getPassword();

        String searchQuery = "SELECT * FROM pilots x WHERE x.email = '" + username + "' AND x.password = '" + password + "'";

        System.out.println("Your user name is " + username);
        System.out.println("Your password is " + password);
        System.out.println("Query: " + searchQuery);

        try {
            currConn = ConnectionManager.getConnection();
            stmt = currConn.createStatement();
            rs = stmt.executeQuery(searchQuery);
            boolean more = rs.next();

            if (!more) {
                System.out.println("Sorry, you are not a registered user! Please sign up first");
                userBean.setValid(false);
            } else {
                String firstName = rs.getString("FIRST_NAME");
                String lastName = rs.getString("LAST_NAME");

                System.out.println("Welcome " + firstName);

                userBean.setFirstName(firstName);
                userBean.setLastName(lastName);
                userBean.setValid(true);
            }
        } catch (Exception ex) {
            System.out.println("Log In failed: An Exception has occurred! " + ex);
            ex.printStackTrace();
        } finally {
            if(rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(currConn != null){
                try {
                    currConn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return userBean;
    }
}

UserBean.java(模型,用作 DTO 的经典 POJO/bean)

//...
public class UserBean {
 private String username;
 private String password;
 private String firstName;
 private String lastName;
 private boolean valid;

 public String getUsername() {
  return username;
 }

 public void setUsername(String username) {
  this.username = username;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public boolean isValid() {
  return valid;
 }

 public void setValid(boolean valid) {
  this.valid = valid;
 }
}

userLogged.jsp (exitpoint - view) -- 别管 div 元素-

<%@ page language="java" contentType="text/html; charset=windows-1250" pageEncoding="windows-1250"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <link rel="stylesheet" type="text/css" href="mystyle.css" />
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <title>Successful login!</title>
</head>
<body>
 <div id="page">
  <div id="content_container">
   <div id="content">
   <jsp:useBean id="currentSessionUser" class="examplePackage.UserBean" scope="application">
    Welcome, <jsp:getProperty name="currentSessionUser" property="username"/> <br>
    ********<br>
    Test 0 -> ${param.name}<br>
    Test 1 -> ${paramValues.name[0]}<br>
    Test 2 -> ${paramValues[name[0]]}<br>
    Test 3 -> ${param["name"]}<br>
    Test 4 -> ${param.username}<br>
    Test 5 -> ${param["username"]}<br>
    Test 6 -> ${sessionScope.currentSessionUser.username}<br>  
    *******<br>
    Test 7 -> ${header.host}<br>
    Test 8 -> ${header["host"]}<br>
    Test 9 -> ${pageContext.request.method}<br>
   </jsp:useBean>
   </div>
  </div>
 </div> 
</body>
</html>

网页输出如下(c/p直接来自FireFox):

Welcome, USER_X
********
Test 0 ->
Test 1 ->
Test 2 ->
Test 3 ->
Test 4 ->
Test 5 ->
Test 6 -> USER_X
*******
Test 7 -> localhost:8080
Test 8 -> localhost:8080
Test 9 -> GET

1) 我的第一个问题是关于范围 - 哪个范围实际适用?如果您检查 userLogged.jsp,第 13 和 22 行(L13 和 L22),您会看到我的困境 - 如果我在 L13 中使用“应用程序”以外的任何其他范围,L14 将返回空值。另一方面,如果我在 L22 上使用 applicationScope,它会返回 null(因为它应该很好,因为我设置的是 SESSION 属性,而不是上下文属性!)。 所以,问题是 - 为什么我应该在 L13 上使用应用程序范围?从我的控制器可以看出,除了会话范围之外,我什么都不期待。

2) 另一个问题是关于 EL - 为什么我不能在测试 0-5 中获取请求参数?其他东西工作正常(从输出中可以看出),但我不明白如何让这些请求参数按照我的意图打印出来(通过请求 EL 隐式对象)。

3) 我也很好奇为什么如果我使用它(userLogged.jsp 的L24,将属性更改为property="*")这将不起作用?

Welcome, &lt;jsp:getProperty name="currentSessionUser" property="*"/&gt;

它返回 null,并且我已经根据 JavaBeans 规范匹配了我的域对象 (UserBean) 属性。我希望它会返回与 LoginPage.jsp 中的输入类型字段匹配的所有 userBean 属性,并且具有正确的类型以使用该功能(必须是字符串或原始类型)。

非常感谢您

关于 EK

【问题讨论】:

    标签: java jsp scope attributes el


    【解决方案1】:

    您不需要jsp:useBeanjsp:getProperty。摆脱他们。您已经在使用 servlet,并且已经将登录用户置于会话范围内,并在此行中使用键 currentSessionUser

    session.setAttribute("currentSessionUser", user);
    

    显示用户名所需的操作如下:

    <p>Welcome, ${currentSessionUser.username}</p>
    

    要防止 XSS,请使用 JSTL c:out

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    ...
    <p>Welcome, <c:out value="${currentSessionUser.username}" /></p>
    

    你肯定不想把它放在应用程序范围内。它将应用于所有网站访问者。


    关于您的请求参数问题:您正在使用response.sendRedirect() 触发重定向。这基本上会指示网络浏览器在给定的 URL 上创建一个全新的请求。您没有传递任何参数,因此它在重定向请求中确实不可用。这一切都按预期工作。如果您希望原始请求参数在结果页面中仍然可用,那么您应该使用RequestDispatcher#forward()

    转发请求
    request.getRequestDispatcher("page.jsp").forward(request.response);
    

    或沿重定向传递参数

    response.sendRedirect("page.jsp?param1=" + URLEncoder.encode(param1, "UTF-8"));
    

    顺便说一句,您的 DAO 代码中有一个主要问题:它不是线程安全的。连接和结果集被声明为static。也容易出现资源泄露,finally没有关闭。


    另见:


    更新根据 cmets:

    当您在 EL 上下文中使用${key} 引用一个对象时,它会在后台使用JspContext#findAttribute() 分别在页面、请求、会话和应用程序范围内定位相关值,并返回第一个非空值。

    至于jsp:useBean,您基本上是在应用程序范围内定义一个新的bean,而不是在会话范围内引用现有的bean。如果您在应用程序范围内将其显式引用为${applicationScope.currentSessionUser},您将看到它返回的结果与您在 servlet 的会话范围内设置的不同。您需要将scope="application" 替换为scope="session"

    对于property="*",这仅在您转发之前回答的请求时有效。这些将从请求参数中设置。

    不,finally 肯定不是反模式。然而,它是初学者中最容易被误解/低估的关键词之一。 finally 块不会使其成为线程安全的。它可以防止资源泄漏。删除static 并在方法本地块中声明资源将使其成为线程安全的。关于 DAO 模式,您可能会发现 this article 很有用。

    【讨论】:

    • 感谢您的回复和链接。 1) 我认为我不需要 jsp:x,但在学术上对它们的工作方式很感兴趣(我正在为 SCWCD 做准备)。到目前为止,我不明白放在会话范围内的东西如何在 CONTEXT 范围内也可用....我(误会?)明白这是不可能的吗?在上下文中使用 currentSessionUser 确实感觉错误,这就是为什么我惊讶地看到它的行为。 2)太好了,谢谢,我会尽快尝试的……既然你这么说,这一切都说得通了。 3) 关于property="*",您对此有什么额外的想法吗?
    • 最后一点 - 关于线程安全:我实际上有点意识到这一点,但不知道如何解决。在筛选了一些站点之后,我的印象是finally 实际上有点 Java 反模式,应该尽可能避免。您在这里有其他建议吗?
    • 1.我不知道我是否清楚地表达了它,但是关于jsp:useBean - 我实际上是这样做的(用会话范围替换应用程序范围)并且惊讶地看到返回 null,因此我的问题(你可以看到 USER_X 出现在两者中会话和应用范围,我不明白为什么)。 2.我还添加了一个finally 块,我相信这应该使它成为线程安全的?您是否有更好的方法来解决这个问题,我真的不喜欢静态引用,并且不知何故觉得有更好的方法来做到这一点。
    • 检查答案更新。至于jsp:useBean 问题:要么您不在同一个会话中,要么您仍然做错了,或者您没有运行您认为正在运行的代码。毕竟,在这种特殊情况下,那件事完全是多余的。
    • 非常感谢您的努力 - 我会稍微摆弄一下,当然会回来提出新问题 :)
    【解决方案2】:

    如果你使用带有class 属性的jsp:useBean,一个新的bean 将被实例化并放在请求的范围内。例如,要重用某个范围内已经可用的 bean,您必须使用 type 属性并将范围设置为“会话”。 http://java.sun.com/products/jsp/tags/11/syntaxref11.fm14.html 描述了不同属性之间的关系。

    【讨论】:

    • 谢谢你,我相信我明白这一点,但你能试着具体回答我提出的每一个观点吗?我不太明白你想说什么。
    • 正如 BalusC 所指出的,您的代码中实际上并不需要 jsp:useBean。我想他已经回答了你问题的所有部分。
    猜你喜欢
    • 2020-02-16
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    • 2020-06-13
    • 2020-05-21
    • 1970-01-01
    相关资源
    最近更新 更多