实现用户登录
一、添加框架
1、添加jar commons-fileupload commons-io javassist commons-lang3 freemarker ognlstruts2-core log4j
2、配置 web.xml 前端控制器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> |
3、定义struts2的核心配置文件
创建src/struts.xml文件
在struts2-core-2.5.16.jar/ struts-2.5.dtd中复制
<!DOCTYPEstruts PUBLIC
"-//Apache Software Foundation//DTDStruts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
到struts.xml
配置如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="aaa" extends="struts-default"></package> </struts> |
4部署项目到服务器中,启动服务器,查看控制台报错信息,从而判定框架的准备工作是否完成。
5、定义欢迎首页
配置web.xml
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> |
6、定义页面 index
<%@ page language="java" pageEncoding="GBK"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <a href="loginput.action">登录系统</a> </body> </html> |
1、 请求loginput.action打开输入页面
问题1:Struts2中默认请求控制器的地址后缀为.action或者没有后缀
这是在框架中通过常量定义的 stuts2-core.jar中/org/apache/struts2/default.properties
如果修改,可以在sturts.xml中通过常量配置标签进行修改
如果定义多个后缀,中间用逗号隔开,例如dd,bb,cc
2、 定义控制器
a) Struts2是一个实现了MVC模式的框架
b) Model:Action(业务控制器),运行特征为多实例单线程的运行方式
c) View:Struts2默认提供了4种视图技术(jsp xsltvelocity freemarker),同时提供了Result接口允许用户自定义扩展。学习过程中使用的是jsp,建议大家抽额外时间学习freemarker
d) Controller:前端控制器StrutsPrepareAndExecuteFilter+一组拦截器(当定义的控制器所在包继承struts-defualt时自动引入了一组拦截器)
定义控制器有3种方法:
方法1:不继承任何类不实现任何接口 ---体现了struts2的轻侵入性
缺陷:方法名称错误在编译器不能发现
返回的逻辑地址名没有规则
方法2:引入Action接口
publicinterface Action { Action接口中定义了5个常量用于规范逻辑地址名;定义了抽象方法用于规范方法签名 publicstaticfinal String SUCCESS = "success";表示控制器中执行正常,跳转到成功页面
publicstaticfinal String NONE = "none";表示控制器中执行正常,但是不跳转任何页面.一般用于测试 publicstaticfinal String ERROR = "error";表示控制器中执行中出现严重错误,需要跳转到错误处理页 publicstaticfinal String INPUT = "input";表示设置对应的输入页面,一般用于输入的报错显示 publicstaticfinal String LOGIN = "login";设置登录输入页面
public String execute() throws Exception;抽象方法,如果类实现了这个接口则必须实现这个方法。如果方法签名错误则编译报错 } |
方法3:『推荐使用』struts2为了进一步简化Action的定义方法,提供了一个父类ActionSupport
public class ActionSupport implements Action, 实现了Action接口,则5大常量和execute方法签名有效 Validateable, ValidationAware,对于服务器端数据校验提供了支持 TextProvider, LocaleProvider,对于国际化I18N、本地化L10N Serializable序列化接口 ActionSupport类中提供了execute方法的默认实现,不做任何处理直接跳转到success对应的页面 |
配置方法: 规则:在<action>中如果不配置class属性时,则默认class为ActionSupport 如果当一个请求地址不需要进行任何处理时,可以使用上面的规则,避免定义Action类 访问loginput.action时直接跳转到login.jsp页面 |
如何在action中接收用户提交数据?
方法1:属性驱动
页面 | Action类 |
<input name="username"/> | public class LoginAction extends ActionSupport{ private String username } get/set方法 |
传递数据的依据为属性名称对应 在页面中的输入项名称username对应action类中的属性username,两个名称一致即可 |
方法2:模型驱动[推荐使用]
可以将一组数据直接填充到复杂类型的属性中
页面 | Action类 |
<input name="username"/> | Public class LoginAction implements ModelDriver<UserBean>{模型驱动,需要实现getModel方法 Private UserBean user=new UserBean(); } |
对应接收数据的值bean,例如UserBean Public class UserBean{ private String username; } |
提交的username\password等请求参数,会按照参数名称对应值bean属性名称的方式注入到bean属性中.如果提交的请求参数名称没有对应的属性(例如login.action?uname=3333),则bean属性中不能接受到参数(也就是说UserBean中没有uname属性)。
方法3:对象驱动的方式,这种方式一般不推荐
<input name=”user.username”/>其中user对应的是Action中的UserBean类型的属性user |
Public class UserAction{ Private UserBean user;//使用属性驱动可new也可不new,当前Action类不需要实现ModelDriven接口,对应的user属性上添加get/set方法即可 } |
Public class UserBean{ Private String username; } |
个人运用:
import org.apache.commons.lang3.StringUtils; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import dao.IUserDao; import entry.UserBean; import util.DaoFactory; public class LoginAction extends ActionSupport implements ModelDriven<UserBean> { private static final long serialVersionUID = -1085416037846142329L; private UserBean user = new UserBean(); private IUserDao userDao = DaoFactory.getUserDao(); @Override public String execute() throws Exception { boolean bb = userDao.login(user); if (bb) { return SUCCESS; } else { this.addActionError("登录失败!请重新登录");// 定义action级别的报错信息,在页面中可以使用标签<s:actionError/>进行显示 return INPUT; } } public void validate() { if(user.getUsername()==null||user.getUsername().trim().length()< 1) this.addFieldError("username", "同户名不能为空!"); if (StringUtils.isEmpty(user.getPassword())) this.addFieldError("password", "用户口令不能为空!"); } public UserBean getModel() { return user; } } |
问题:提交数据的中文乱码问题?
Struts2中有个运行时常量 struts2-core.jar中
/org/apache/struts2/default.properties
定义的是struts2中所采用的默认编码字符集
解决方法1:【推荐】
所有的页面统一使用utf-8编码,提交的信息中最好不包含中文,如果包含中文请使用post的提交方法
<%@ pagelanguage="java" pageEncoding="UTF-8"%>
方法2:如果需要修改编码
则在struts.xml中重新定义常量即可
<constantname=”struts.i18n.encoding”value=”GBK”></constant>
然后所有的页面都是用相同的编码字符集即可,提交的信息中最好不包含中文,如果包含中文请使用post的提交方法
编码实现服务器端数据校验
在action类中定义方法validate()可以编码实现服务器端数据校验
public void validate() { if (user.getUsername() == null || user.getUsername().trim().length() < 1) this.addFieldError("username", "同户名不能为空!"); if (StringUtils.isEmpty(user.getPassword())) this.addFieldError("password", "用户口令不能为空!"); } |
addFieldError(“username对应的就是输入域的name,用于决定未来的报错显示位置”,”具体的报错提示信息”):用于添加和输入域相关的报错信息
报错:
Validate方法在数据提交的执行流程:
1、 创建action对象 Class.forName(“”).newInstance()
2、 调用set方法注入请求参数
a) 采用的是模型驱动则使用的是getModel().setXXX
3、 调用validate方法,
a) 如果添加了报错信息addFieldError时,则自动转向到input,不会调用execute方法
b) 如果没有添加报错信息,则继续执行execute()方法
报错信息的显示问题?
定义页面时可以不使用Struts2的标签库,但是Struts2为了简化页面也提供了标签库,使用这个标签库可以方便页面的编写
<%@page import="java.util.Enumeration"%> <%@ page language="java" pageEncoding="UTF-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <s:head /> </head> <body> <s:actionerror /> <s:form action="login.action" method="post"> <s:textfield name="username" label="用户名称" /> <s:password name="password" label="用户密码" /> <tr> <td colspan="2"><s:submit value="登录系统" theme="simple" /> <s:reset value="重置数据" theme="simple" /> </td> </tr> </s:form> </body> </html> |
具体显示中位置出错?
Struts2标签库中提供了4种主题:xhtml(默认主题,采用<table>进行显示控制) css_xhtml simple ajax
<form id="login" name="login" action="login.action" method="post"> <table class="wwFormTable"> Username:<tr> <td class="tdLabel"></td> <td class="tdInput" ><input type="text" name="username" value="" id="login_username"/></td> </tr>
<br/> <input type="submit" value="Login"/> </table></form> |
显示效果:
添加一个标签<s:head/>
会使用红色显示报错信息
如果需要人为控制报错显示,可以使用标签<s:fielderror/>
<s:fielderror/>显示所有的输入域相关的报错信息
<s:fielderrorfieldName="username"/>显示和username输入域相关的报错信息
标签的默认主题为xhtml
默认按钮是一行一个按钮,如果需要进行调整,可以在标签中使用theme属性设置所使用的主题
实现用户登录
1、定义欢迎首页
<ahref="loginput.action">登录系统</a>
2、定义配置,使不通过定义控制器类的方式实现跳转页面[不允许直接从页面跳转到页面] struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <constant name="struts.action.extension" value="action"/> <package name="aaa" extends="struts-default"> <action name="login" class="action.LoginAction"> <result>/welcome.jsp</result> <result name="input">/login.jsp</result> </action> <action name="loginput"> <result name="success">/login.jsp</result> </action> </package> </struts> |
3、 定义页面,可以使用stuts2的标签库简化页面编写
4、定义控制器接受客户端提交数据
4.1、定义接收用户提交数据的值bean
import java.io.Serializable; public class UserBean implements Serializable { private static final long serialVersionUID = -5234379269023696981L; private long id; private String username; private String password; public long getId() { return id; } public void setId(long id) { this.id = id; } 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; } @Override public String toString() { return "UserBean [username=" + username + "]"; }
} |
4.2、定义控制器
定义服务器端数据校验方法
部署应用并提交空数据进行校验
如果输入了用户名称,但是没有输入用户口令情况
import org.apache.commons.lang3.StringUtils;
import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven;
import dao.IUserDao; import entry.UserBean; import util.DaoFactory;
public class LoginAction extends ActionSupport implements ModelDriven<UserBean> {
private static final long serialVersionUID = -1085416037846142329L; private UserBean user = new UserBean(); private IUserDao userDao = DaoFactory.getUserDao();
@Override public String execute() throws Exception { boolean bb = userDao.login(user); if (bb) { return SUCCESS; } else { this.addActionError("登录失败!请重新登录");// 定义action级别的报错信息,在页面中可以使用标签<s:actionError/>进行显示 return INPUT; } }
public void validate() { if(user.getUsername()==null||user.getUsername().trim().length() < 1) this.addFieldError("username", "同户名不能为空!"); if (StringUtils.isEmpty(user.getPassword())) this.addFieldError("password", "用户口令不能为空!"); }
public UserBean getModel() { return user; } } |
发现<s:form>支持数据回填功能,但是需要知道:密码域永不回填
4.3、定义execute方法实现用户登录的逻辑
使用DAO模式实现username和password的数据库查询
1个接口 若干实现 1个值bean 1个工厂
接口—隔离实现,上层的action调用dao时是通过接口进行调用,不是直接访问实现类
public interface IUserDao { public boolean login(UserBean user) throws Exception; } |
定义值bean
定义dao接口的具体实现
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet;
import com.mysql.jdbc.Driver;
import entry.UserBean;
public class UserDaoImpl implements IUserDao { public boolean login(UserBean user) throws Exception { boolean res = false; Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { new Driver(); conn = DriverManager.getConnection("jdbc:mysql:///users?useSSL=false&serverTimezone=UTC", "root", "root"); ps = conn.prepareStatement("select * from t_users where username=? and password=?"); ps.setString(1, user.getUsername()); ps.setString(2, user.getPassword()); rs = ps.executeQuery(); if (rs.next()) { user.setId(rs.getLong("id")); res = true; } } finally { try { if (rs != null) rs.close(); } finally { try { if (ps != null) ps.close(); } finally { if (conn != null) conn.close(); } } } return res; } } |
定义工厂类
public class DaoFactory { public static IUserDao getUserDao() { return new UserDaoImpl(); } } |
在Action中通过dao接口访问数据库
Action从功能的角度上说,经常叫做业务控制器,但是不是控制器,是模型,运行特征为多实例单线程
private UserBean user = new UserBean(); private IUserDao userDao = DaoFactory.getUserDao();
@Override public String execute() throws Exception { boolean bb = userDao.login(user); if (bb) { return SUCCESS; } else { this.addActionError("登录失败!请重新登录");// 定义action级别的报错信息,在页面中可以使用标签<s:actionError/>进行显示 return INPUT; } } |
在页面上添加报错显示处理
<s:actionerror/>
定义成功显示页面welcome.jsp
在struts.xml中进行配置
<result>/welcome.jsp</result> |
定义表结构
在cmd mysql –uroot –p
Create table if not exists t_users(
Id bigint primarykey auto_increment,
Usernamevarchar(20) not null unique,
Passwordvarchar(20) not null
)engine=innodb default charset utf8;
插入数据:insertinto t_users values(01,”luck”,”123456”);
测试登录