JBuilder2005 Struts深度体验之改造

作者:网络 来源:佚名 更新时间:2008-07-07 12:23:22 点击:
中国最大的web开发资源网站及技术社区,

  用actionform截获登录表单数据

  原登录模块用bookstore.user描述login.jsp登录页面表单的数据,在switch.jsp程序中通过<jsp:usebean>标签获取login.jsp表单的数据。其实user类相当于struts框架中的模型,我们将通过一个actionform更好地实现这个功能。

  actionform和bean一样以属性名匹配的映射机制从http请求中填充对象数据,但actionform比一般的bean提供了更多的功能,struts允许actionform通过validate()方法进行自校验,当数据不合法时自动转向到输出界面,此外还可以通过reset()方法,在数据填充前复位属性值。

  下面我们就来创建useractionform,替换user的功能,建立起struts框架中的"数据模型"。

  1.指定actionform的web模块及类信息

  file->new...->web->在web页中双击actionform图标,弹出如图 6所示的对话框:


图 6 创建useractionform

  ·struts config:我们前面有提到struts1.1支持多个配置文件,所以你在这儿可以选择使用哪个struts配置文件。因为我们现在还没有定义多个struts配置文件,所以只得使用struts-config.xml。在开发新增图书的功能时,我们将定义另一个配置文件。

  ·actionform:actionform的类名,这里我们填入useractionform。

  按next到下一步。

  2.定义actionform属性


图 7 定义actionform属性

  通过add...按钮为useractionform增加4个属性,如图 7所示。特别的,如果这个actionform所对应的入口页面已经创建,你也可以直接通过add from jsp...按钮,选择一个jsp页面,jbuilder会分析这个页面的表单,并将表单的数据组件名抽取为actionform的属性。

  按next到下一步。

  3.一些附加功能的定义


图 8 附加功能定义

  在formbeanname中为useractionform指定一个名字,一般接受jbuilder所提供的默认名即可。这个名字将在struts-config.xml文件用来命名useractionform。

  actionform比一般javabean强大的地方在于它可以进行数据检验,还可以进行数据复位。如果这个actionform最终要放到session中的,那么最好实现reset()方法,以复位actionform的数据,否则属性可能不会反映最新的值。这里, useractionform无需进行数据有效性校验,但由于useractionform最终需要放到session中,所以我们需要实现reset()方法。故此我们勾选create/replace reset() method body选项。

  直接按finish创建useractionform,再将user类的代码拷贝过来,整改后的最终代码如下所示:

  代码清单 6 以actionform实现的user类

1. package bookstore;
2.
3. import java.sql.*;
4. import java.text.*;
5. import java.util.date;
6. import javax.servlet.http.*;
7. import org.apache.struts.action.*;
8.
9. public class useractionform
10. extends actionform
11. {
12.  private string userid;
13.  private string password;
14.  private string username;
15.  private string logindatetime;
16.  public string getpassword() {
17.   return password;
18.  }
19.
20.  …
21.  //复位所有属性值
22.  public void reset(actionmapping actionmapping,httpservletrequest servletrequest) {
23.   this.userid = null;
24.   this.username = null;
25.   this.password = null;
26.   this.logindatetime = null;
27. }


  此外,jbuilder自动在struts-conf.xml文件中通过<form-bean>描述actionform。useractionform必须和一个action相关联,因为http请求通过struts总控制器转发给action,struts控制器一旦发现action有一个对应的actionform时,就用http请求的数据填充这个actionform。

  用action代替switch.jsp的控制转换功能

  我们在前面已经数落用switch.jsp实现请求转换控制的缺点,struts框架的action是实现请求转换控制的最适合替代者。

  在这节里,我们就来创建一个名为loginaction的action,让其完美的接替switch.jsp的工作。

  file->new...->web->在web页中双击action图标,启动创建action的向导。

  1.指定action名字及web模块


图 9 指定web模块及action名字

  在action中键入loginaction作为action的类名,其中base class的下拉框中有许多action基类可供选择,它们用于不同的场合,这些选项是:

  ·org.apache.struts.action.action:标准的action。

  ·org.apache.struts.actions.forwardaction:相当于jsp的<jsp:forward>,方便struts控制器进行预处理。此外,从学究的角度上来说,在jsp页面直接通过<jsp:forward>违反了mvc的分层原则,控制器无法干预。

  ·org.apache.struts.actions.includeaction:出于forwardaction相似的原因,struts推荐用includeaction代替jsp的<jsp:include>。

  ·org.apache.struts.actions.lookupdispatchaction:如果一个表单有多个提交按钮,不同的提交按钮执行不同的业务操作,用dispatchaction最为合适。

  ·org.apache.struts.actions.switchaction:用switchaction可在不同的struts模块间转换。

  由于我们的action需要完成用户密码验证的业务,并根据结果转换到不同的页面中,所以这个loginaction是一个普通的action,故我们选择org.apache.struts.action.action。

  按next到下一步。

  2.设置action的配置信息


图 10 设置loginaction的配置信息

  ·action path:访问这个action的uri,接受默认的/loginaction,这样我们将通过类似这样的url:http://127.0.0.1:8080/webmodule/loginaction.do来访问这个action。

  ·formbean name:下拉框中列出web模块中所有的actionform,我们选择前一小节中所创建的useractionform。这样客户端的http请求访问loginaction时,http请求所带的数据就会被struts总控制器自动填充到useractionform中了。

  ·scope:action有两个选择:request和session。表示actionform在填充后将放在request对象中还是session对象中,由于我们需要在通过密码验证后,才使用户登录系统。这样就不能使useractionform在数据填充时就放入session中,而应该在通过密码验证后,手工将其绑定到session中(useractionform一旦绑定到session中,其valuebound()方法就会被调用,记录用户登录日志),故此,我们选择request。

  ·input jsp:输入的jsp页面。在actionform需要进行数据有效性自校验的情况下,如果校验失败,struts框架总控制器将请求返回到这个输入页面上。因为useractionform无需进行有效性校验(在3.1的第3步我们没有为useractionform实现自校验功能),所以无需指定输入的jsp。

  按finish按钮直接创建loginaction,jbuilder自动打开struts config editor,生动形象地展现用户登录模块struts框架下的处理流程,如图 11所示:


图 11 struts config editor

  位于中心的/loginaction是访问loginaction的uri,它是登录业务的控制器。struts总控制器创建一个useractionform实例,并用http请求的数据填充useractionform实例,然后将其传给loginaction的execute()方法。

  3.定义访问入口

  现在我们需要调整login.jsp表单的提交地址,使用loginaction来处理用户登录的请求,调整后的代码如所示:

  代码清单 7 login.jsp 使用loginaction处理用户登录

1. <%@page contenttype="text/html; charset=gbk" import="bookstore.userlist" %>
2. …
3. <form name="form1" method="post" action="/webmodule/loginaction.do">用户名:
4.  <select name="userid">
5.   <option value="" selected>--登录用户--</option>
6.    <%=userlist.getuserlisthtml()%>
7.  </select>
8.  密 码:
9.  <input name="password" type="password">
10.   <input type="submit" name="submit" value="登录">
11. </form>
12. </body>
13. </html>


  如第3行所示,将原来action="switch.jsp"改为"/webmodule/loginaction.do",由于我们需要将整个应用部署于/webmodule的uri下,所以需要在action访问的地址前加上/webmodule。如果通过struts的<html:form>标签来指定表单提交的地址,则无需添加/webmodule,标签将自动进行转换,你将在本专题后续内容中学习到这种方法。

  注意:

  struts框架总控制器servlet通过路径匹配的方式截获http请求,其匹配串是*.do,表示url以.do结束的http请求才会被struts框架处理,否则struts忽略之。所以在写链接地址时千万不要忘了调用地址后加一个.do的后缀。


  对login.jsp做调整后,重新切换到/loginaction的struts config editor中,你将看到如图 12所示的流程图:


图 12 在jsp中指定调用action后的流程图

  jbuilder将分析web模块中所有jsp文件,如果发现引用了/loginaction就将其添加到该图中来,作为其访问入口。

  4.为/loginaction定义两个出口

  一个action一般只有一个入口,但往往会有多个出口,action根据业务处理的不同结果转向相应的出口。图 12 /loginaction右边是一个带"forward"的浅色虚框,右键单击这个forward虚框,在弹出的菜单中点击add forward菜单项,在strut config editor中将新增一个默认名为forward的出口项图标,左键单击这个forward新增的图标,对这个出口进行制定,如图 13所示:


图 13 为action定义出口

  我们为这个出口地址取名为success,点击path后的…按钮弹出browser for path对话框,列出当前web模块所有可作为出口地址,如图 14所示:


图 14 可选出口地址

  我们选择welcome.jsp作为success的出口地址,按ok确定。

  按相同的方法再为/loginaction创建一个名为fail出口地址为fail.jsp以及名为error出口地址为error.jsp两出口,最后登录模块的流程如图 15所示:


图 15 登录模块的最终流程

  后面,我们将在loginaction通过代码根据用户验证成功与否决定程序的出口,你将会发现我们通过出口的名字引用出口的地址。
完成以上配置后,切换到source标签页,struts-config.xml文件中悉数记录下了这个配置信息:

  代码清单 8 登录模块对应struts-config.xml的配置信息

1. <struts-config>
2.  <form-beans>
3.   <form-bean name="useractionform" type="bookstore.useractionform" />
4.  </form-beans>
5.  <action-mappings>
6.   <action name="useractionform" path="/loginaction"
7.        scope="request" type="bookstore.loginaction">
8.    <forward name="success" path="/welcome.jsp" />
9.    <forward name="fail" path="/fail.jsp" />
10.   <forward name="error" path="/error.jsp" />
11.   </action>

12.  </action-mappings>
13. </struts-config>


  其中第3行的配置信息声明了useractionform,为其指定了一个名字,在第6~10行是/loginaction的配置信息,它通过name属性声明这个action对应的actionform为useractionform。

  在第8~10行,3个出口各对应一个<forward>配置项,在loginaction中我们将通过<forward>的name属性引用出口的地址。

  下面,我们调整loginaction类的execute()方法的代码,在其中验证用户密码,并根据验证结果转向不同的出口,其最终代码如下所示:

  代码清单 9 loginaction.java

1. package bookstore;
2.
3. import org.apache.struts.action.actionmapping;
4. import org.apache.struts.action.actionform;
5. import javax.servlet.http.httpservletrequest;
6. import javax.servlet.http.httpservletresponse;
7. import org.apache.struts.action.actionforward;
8. import org.apache.struts.action.action;
9.
10. import java.sql.*;
11.
12. public class loginaction
13. extends action {
14.  public actionforward execute(actionmapping actionmapping,
15.    actionform actionform,
16.    httpservletrequest servletrequest,
17.    httpservletresponse servletresponse) {
18.     useractionform useractionform = (useractionform) actionform;
19.      connection conn = null;
20.      try
21.     {
22.      conn = dbconnection.getconnection();
23.      preparedstatement pstat = conn.preparestatement(
24.        "select user_name from t_user where user_id=? and password = ?");
25.      pstat.setstring(1, useractionform.getuserid());
26.      pstat.setstring(2, useractionform.getpassword());
27.      resultset rs = pstat.executequery();
28.      if (rs.next())
29.      { //密码正确
30.       useractionform.setusername(rs.getstring(1));
31.       servletrequest.getsession().setattribute("ses_userbean", useractionform);
32.       return actionmapping.findforward("success");//通过验证,转向welcome.jsp出口
33.      }
34.     }
35.     catch (sqlexception se)
36.     {
37.      se.printstacktrace();
38.      return actionmapping.findforward("error");//程序发生异常,转向error.jsp出口
39.     }
40.     finally
41.     {
42.      try
43.      {
44.       if (conn != null)
45.       {
46.        conn.close();
47.       }
48.      }
49.      catch (sqlexception ex)
50.      {
51.       ex.printstacktrace();
52.       return actionmapping.findforward("error");//程序发生异常,转向error.jsp出口
53.      }
54.     }
55.     return actionmapping.findforward("fail");//未通过验证,转向fail.jsp出口
56.    }
57. }


  在第18行通过强制类型转换获取useractionform实例,其后验证用户的代码其实就是switch.jsp验证用户的scriptlet的代码。我们根据用户验证的结果通过actionmapping将请求转向不同的出口(如第32、38、52、55行所示),其中findforward(name)中的name即是struts-config.xml中对应action配置项的<forward>中指出的出口项名字。这种通过名字引用出口的调用方式给我们带来了很大的灵活性,因为它将流程逻辑和具体实现隔离开来,假设你不希望用welcome.jsp作为登录成功所转向的页面,你只要在配置文件中对success的出口配置项进行调整就可以了,而无需更改程序。

  在通过用户验证后,我们将useractionform手工放到session中(第31行),以ses_userbean为名放入session时useractionform的valuebound()方法会被触发调用,记录用户登录日志。由于原success.jsp包含下面的代码:

<jsp:usebean id="ses_userbean" scope="session" class="bookstore.user"/>


  因为此时,我们已经用useractionform替换原user类,所以需要对这行代码作调整,否则在进行强制类型转换时将发生classcastexception异常。调整后的代码为:

<jsp:usebean id="ses_userbean" scope="session" class="bookstore.useractionform"/>


  提示:

  一般情况下,action只执行流程控制的功能,而不执行具体的业务处理。所以loginaction的execute()中验证用户的业务最好抽取到一个具体的bo中(business object:商业处理对象)。