JSP中使用getAttribute获取的useBean为空,由servlet引起。

6

在servlet中,用户为空。如果我做错了,请告诉我。

我试图获取bean rateCode.jsp中的所有HTML元素。

<%@page import="com.hermes.data.RateCode_" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
    <head>
        <title>Rate Code</title>
    </head>
    <body>      
         <jsp:useBean id="user" class="com.hermes.data.RateCode_" scope="request" >
            <jsp:setProperty name="user" property="*"/></jsp:useBean>
            <form  id="f_rateCode" action="/ratePromoCodes" method="post"  >
                <table align="center" border="1" cellspacing="0">
                    <tr>
                        <td colspan="2" align="center" class="header">Rate Code Administrations</td>
                    </tr>
                    <tr>
                        <td align="right" style="border-style: solid;">Rate Code:</td>
                        <td align="left" style="border-style: solid;">
                            <input type="text" id="code" name="code" value="${user.code}"  size="10" maxlength="32" style="width: 100px"/>
                    </td>
                </tr>

                <tr>
                    <td align="right" style="border-style: solid;">Rate Description:</td>
                    <td align="left" style="border-style: solid;">
                        <input type="text" id="description" name="description" value="<%=user.getDescription()%>" maxlength="128" size="40"></td>
                </tr>              
                <tr><td><input type="submit" value="ok" /></td> </tr>
            </table>
        </form>

Servlet - ratePromoCodes

protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        RateCode_ rc = (RateCode_) req.getAttribute("user");
        Enumeration an = req.getAttributeNames();
        Enumeration pn = req.getParameterNames();
        Object o = null;
        while (an.hasMoreElements()) {
            o = an.nextElement();
            System.out.println(o);
        }
        while (pn.hasMoreElements()) {
            o = pn.nextElement();
            System.out.println(o);
        }
    }

RateCode.java (javaBean)

public class RateCode_  {    
    private String code ;
    private String description;
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
3个回答

10

您好,您似乎误解了 jsp:useBean 的工作原理和目的。

首先,您声明了该 bean 为 session 范围,并用当前请求的所有参数填充了它。

<jsp:useBean id="user" class="com.hermes.data.RateCode_" scope="session">
    <jsp:setProperty name="user" property="*"/>
</jsp:useBean>

这个bean被存储为session属性,名称为user。在servlet中,您需要将其作为session属性而不是请求属性进行检索。

RateCode_ user = (RateCode_) request.getSession().getAttribute("user");

(顺便说一下,“user”是一个糟糕且令人困惑的属性名称,我会将其重命名为“rateCode”或其他名称,而不是以这种奇怪的方式以“_”结尾)

然而,它将不包含任何内容。在您尝试在servlet中访问它之前,getCode()getDescription()将返回null。即,在此时点上,<jsp:setProperty>并没有用所有请求参数填充它。只有当您将包含参数的请求转发回JSP页面时,它才会这样做。但是,这超出了servlet中的业务逻辑。

您需要自己收集它们作为请求参数。首先,在servlet的doPost()方法中摆脱整个<jsp:useBean>的东西,然后执行以下操作:

RateCode_ user = new RateCode_();
user.setCode(request.getParameter("code"));
user.setDescription(request.getParameter("description"));
// ...
request.setAttribute("user", user); // Do NOT store in session unless really necessary.

然后您可以按照以下方式在JSP中访问它:

<input type="text" name="code" value="${user.code}" />
<input type="text" name="description" value="${user.description}" />

(这只对XSS攻击敏感,您可以安装JSTL并使用fn:escapeXml)

不,您在JSP中不需要使用<jsp:useBean>。如果您正在使用具有真实servlet的MVC(级别2)方法,则应将其排除在外,因为它几乎没有价值。 <jsp:useBean>仅适用于MV设计(MVC级别1)。为了节省收集请求参数的样板代码,请考虑使用MVC框架或Apache Commons BeanUtils。有关提示,请参见下面的链接。

另请参阅:


1
@Ryan:不,它在两者中都可以使用。但是在使用servlet时没有意义。它对于基本/初学者的无servlet JSP-only webapp很有帮助。您只需要了解其生命周期即可。它不提供任何控制请求/响应的方法。只能预处理/后处理数据,仅此而已。您可以hack一个getter来执行业务操作方法并返回结果,但这很丑陋。Servlet为您提供了一种干净的方式来控制请求/响应并调用操作。如果您的唯一目的是在servlet中填充bean,请检查commons BeanUtils或仅使用JSF等MVC框架。 - BalusC
1
@Ryan:请参考以下链接:https://dev59.com/KVTTa4cB1Zd3GeqPt53V 和 https://dev59.com/onA65IYBdhLWcg3w9DqF。 - BalusC
我在想,我可以将DTO与表单绑定,并从JSP将数据发送到Servlet(就像在JSF中从*.xhtmlManagedBean)。因为我正在从DTO对象获取值,例如<input type="text" name="code" value="${user.code}" />。如果我可以从Servlet向JSP发送对象,则可能有一种选项可以从JSP向Servlet发送对象。你怎么看,@BalusC? - Yubaraj
@Yubaraj:是的,很多初学者直觉地认为jsp:useBean会以相同的方式工作。但正如答案中所解释的那样,它并不是这样。只需使用普通的MVC框架即可。 - BalusC
实际上,我现在正在使用仅JSP和Servlet技术开展一个项目,因此尝试基于MVC模式进行开发。即使用JSP页面作为视图,Servlet作为控制器。因此,我尝试将DTO与JSP(HTML)表单绑定,并提出了这个问题/答案。是否有任何@BalusC的选项可供使用? - Yubaraj
显示剩余7条评论

4
问题(及其解决方案)如下:
您创建了一个请求范围的bean“user”,但一旦页面加载完成,请求就结束了 - 难怪在下一个与此无关的请求中它是null。您想做的可能是以下内容:
1)完全从jsp页面中删除<jsp:useBean ...>,使其看起来如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page import="com.hermes.data.RateCode_" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head><title>Rate Code</title></head>
<body>
<form id="f_rateCode" action="/forwarder.jsp" method="post">
    <table align="center" border="1" cellspacing="0">
        <tr>
            <td colspan="2" align="center" class="header">Rate Code Administrations</td>
        </tr>
        <tr>
            <td align="right" style="border-style: solid;">Rate Code:</td>
            <td align="left" style="border-style: solid;"><input type="text" id="code" name="code" value=""
                                                                 size="10" maxlength="32" style="width: 100px"/></td>
        </tr>
        <tr>
            <td align="right" style="border-style: solid;">Rate Description:</td>
            <td align="left" style="border-style: solid;"><input type="text" id="description" name="description"
                                                                 value="" maxlength="128"
                                                                 size="40"></td>
        </tr>
        <tr>
            <td><input type="submit" value="ok"/></td>
        </tr>
    </table>
</form>
</body>
</html>

2)您的表单现在重定向到另一个JSP,即转发器。它看起来如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean id="user" class="com.hermes.data.RateCode_" scope="request"/>
<jsp:setProperty name="user" property="*" />
<jsp:forward page="/ratePromoCodes" />

这段代码的作用是:创建一个请求范围内的bean - 即提交表单的请求。然后使用表单数据填充bean属性,最后将其转发(在同一请求中,这是关键点)到执行某些任务的servlet。

3) 最后,在您的servlet中执行某些操作,我为测试目的执行了以下操作:

public class TestServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        RateCode_ code = (RateCode_) request.getAttribute("user");
        System.out.println(code);
    }
}

谢谢,伙计,它有效了,但为什么它在与servlet一起使用时不起作用。得到null。有没有其他方法可以在JSP中完成,而不需要框架。 - Ravi Parekh
scope="session" 在 SESSION 中也会返回 NULL。 - Ravi Parekh
重点是,如果您希望根据表单输入的值设置bean属性,则必须在提交表单后调用jsp:useBean。 如果您直接转到Servlet,则可以使用会话范围,但是1)必须使用request.getSession()。getAttribute(“user”)2)请注意,如果将<jsp:useBean>放在表单页面中,则bean将尝试在页面加载时填充,当表单为空时。因此,结果bean在servlet中不会为null,但属性不会被设置。 - Jan Zyka
是的,您是正确的。我尝试使用 request.getSession().getAttribute("user"),我发现了 "user" 但 code 和 description 的值为 NULL。 - Ravi Parekh
为此,我们需要为每个页面添加另一个JSP进行转发,如果出现验证错误,则所有HTML表单都将为空,用户必须再次填写。那么有没有办法在同一个JSP中保持它的填充状态呢?这里是链接,但要使用转发和另一个JSP。[链接] http://www.javaworld.com/jw-03-2000/jw-0331-ssj-forms.html?page=3 - Ravi Parekh
你可以检查转发器中的属性。如果它是正常的,你就可以将其转发到原始servlet,否则你就需要将其转发回索引页面。然后,索引页面应该再次具有jsp:useBean和jsp:getProperty作为表单中的值。如果你从转发器转发,bean不会被重新创建,因为已经存在一个bean。你还应该在构造函数中将属性设置为一些有意义的默认值,以免在第一次访问时看到垃圾(null等)。 - Jan Zyka

0

您正在JSP中使用请求范围的bean。该JSP被提交并由Servlet响应。

当执行Servlet时,会启动一个新的“生命周期”,请求范围不包含在JSP中使用/创建的任何请求范围的bean。

您必须将bean的属性作为请求参数提交,并在servlet中逐个读取它们。


1
我们能否将它作为一个Bean(RateCode)获取,这样我就不必一个一个地绑定它了。就像RateCode bean中的所有HTML输入元素一样自动完成。 - Ravi Parekh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接