Dao层
只用实现数据库的查询增删,将判断用户是不是存在这个环节交给service
首先要创建个User类,每个User有username password vorifycode
Dao层实现数据库的增删查询,所有有两个方法 findByUsername(){}和add(){}
对于查询功能
findByUsername返回的是一个user类,相当于把根据名字查询到的用户信息返还给service层,所以传进来的参数应该是String类型
数据库操作首先要创建一个文件解析器SAXReader,对我们的数据库(xml文件)进行read解析
根据数据库的表单形式:
<users>
<user username="李四" password="lisi"/>
<user username="张三" password="zhangsan"/>
<user username="王五" password="wangwu"/>
</users>
我们要获取user元素下有没有username等于需要查询的username的元素,"//user[@username='" + username + "']"
如果得到的元素为空,返还
如果得到的元素不为空,那我们就把这个元素的属性值赋给一个新的User返还
对于增删功能
add无需返还值,但是既然要添加用户,那我们的参数就应该是User
增加很简单,同样需要一个SAXReader解析器,然后通过getrootelement获得根节点,创建一个user元素
元素的属性值分别是user类user.getUsername和user.getPassword方法获得。然后将元素addattribute添加到根节点下
元素增加完后,这只是在内存上进行了修改,我们还需要把文件输出到数据库中保存
为了保证输出时的格式,用一个格式化器,先将空格都消除,然后用\t进行缩进并换行
首先创建一个XMLWriter,参数为一个输出流和格式。把前面修改过的document写出,最后再关闭输出流。
这个Dao层的两个功能就算完成了
package cn.itcast.user.dao;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import cn.itcast.user.domain.User;
/**
* 数据类
* @author cxf
*
*/
public class UserDao {
private String path = "F:/users.xml";//依赖数据文件
/**
* 按用户名查询
* @param username
* @return
*/
public User findByUsername(String username) {
/*
* 1. 得到Document
* 2. xpath查询!
* 3. 校验查询结果是否为null,如果为null,返回null
* 4. 如果不为null,需要把Element封装到User对象中。
*/
/*
* 1. 得到Document
*/
// 创建解析器
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(path);
/*
* 2. 通过xpath查询得到Element
*/
Element ele = (Element)doc.selectSingleNode("//user[@username='" + username + "']");
/*
* 3. 校验ele是否为null,如果为null,返回null
*/
if(ele == null) return null;
/*
* 4. 把ele的数据封装到User对象中
*/
User user = new User();
String attrUsername = ele.attributeValue("username");//获取该元素的名为username属性值
String attrPassword = ele.attributeValue("password");//获取该元素的名为password属性值
user.setUsername(attrUsername);
user.setPassword(attrPassword);
return user;
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
/**
* 添加用户
* @param user
*/
public void add(User user) {
/*
* 1. 得到Document
* 2. 通过Document得到root元素!<users>
* 3. 使用参数user,转发成Element对象
* 4. 把element添加到root元素中
* 5. 保存Document
*/
// 得到Document
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(path);
// 得到根元素
Element root = doc.getRootElement();
// 通过根元素创建新元素
Element userEle = root.addElement("user");
// 为userEle设置属性
userEle.addAttribute("username", user.getUsername());
userEle.addAttribute("password", user.getPassword());
/*
* 保存文档
*/
// 创建输出格式化器
OutputFormat format = new OutputFormat("\t", true);//缩进使用\t,是否换行,使用是!
format.setTrimText(true);//清空原有的换行和缩进
// 创建XmlWriter
XMLWriter writer;
try {
writer = new XMLWriter(
new OutputStreamWriter(
new FileOutputStream(path), "UTF-8"), format);
writer.write(doc);//保存document对象
writer.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
}
既然我们已经实现了数据库的查询增删,那我们就可以提供一些服务——注册和登陆
首先我们先做注册
创建一个regist函数,如果我们要注册,总要知道这个用户注册过来的信息吧,所以传进来的参数应该为一个User
注册我们首先要判断数据库中有没有这个用户,如果没有那我们就调用数据库add方法添加这个用户
如果这个用户已经存在那我们就需要返回一个错误信息,说这个用户已经被注册了。
接着我们要有个UserDao类来调用数据库查询增删的功能
调用findByUsername如果返回值为空,说明这个用户已经存在,那就要抛出一个异常,并说这个用户已经存在
返回值如果不是空,说明注册成功。
接着做登陆
创建一个login函数,同样我们传进来的应该是个User表单(用户和密码即可,但为了简单我们这里统一用User进行封装这个表单),如果登陆成功要进入欢迎界面那我们还需要用户信息,所以返回值也应该是个User,这个User就应该是用户的整个信息
同样利用findByUsername查询数据库是否有这个username,如果返回值为空,说明没有这个人,那么就输出异常:用户不存在
如果返回值不为空,接着对比表单中的密码和数据空中username所对应的密码是否一致,不一致就输出异常:用户名密码错误
用户名密码都正确我们才返还User,即用户的所用信息
package cn.itcast.user.service;
import cn.itcast.user.dao.UserDao;
import cn.itcast.user.domain.User;
/**
* User的业务逻辑层
* @author cxf
*
*/
public class UserService {
private UserDao userDao = new UserDao();
/**
* 注册功能
* @param user
* @throws UserException
*/
public void regist(User user) throws UserException {
/*
* 1. 使用用户名去查询,如果返回null,完成添加
* 2. 如果返回的不是null,抛出异常!
*/
User _user = userDao.findByUsername(user.getUsername());
if(_user != null) throw new UserException("用户名" + user.getUsername() + ", 已被注册过了!");
userDao.add(user);
}
/**
* 登录功能
* @param form
* @return
* @throws UserException
*/
public User login(User form) throws UserException {
/*
* 1. 使用form中的username进行查询,得到User user
*/
User user = userDao.findByUsername(form.getUsername());
/*
* 2. 如果返回null,说明用户名不存在,抛出异常,异常信息为“用户名不存在”
*/
if(user == null) throw new UserException("用户名不存在!");
/*
* 3. 比较user的password和form的password,如果不同,抛出异常,异常信息为“密码错误!”
*/
if(!form.getPassword().equals(user.getPassword())) {
throw new UserException("密码错误!");
}
/*
* 返回数据库中查询出来的user,而不是form,因为form中只有用户名和密码,而user是所有用户信息!
*/
return user;
}
}
注册和登陆功能都实现了,接着就是处理用户数据了,注册和登陆之前我们先要进行表单检验
例如:注册时名字和密码长度限制,登陆时需要验证码。
由于可能存在三种错误,我们将三种错误封装成一个map,键对应的三个可能出错的地方,值对应着异常。
对于用户名和密码1、不能为空,不能只是空格2、长度要在3-15之间
对于验证码1、不能为空2、要正确
现在对错误信息进行处理,如果map集合不为空,则向注册页面转发返还错误信息
如果map集合为空,说明表单校验没有出现问题,接下来进行注册,调用service的注册功能,并对注册页面输出注册成功信息
这里用输出写一条html语句,“注册成功”+跳转登陆页面,不是以原样输出的,需要编译的要加“”
package cn.itcast.user.web.servlet;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.commons.CommonUtils;
import cn.itcast.user.domain.User;
import cn.itcast.user.service.UserException;
import cn.itcast.user.service.UserService;
public class RegistServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// 依赖UserServlet
UserService userService = new UserService();
/*
* 1. 封装表单数据(封装到User对象中)
*/
User form = CommonUtils.toBean(request.getParameterMap(), User.class);
/*
* 添加新任务(表单校验)
* 1. 创建一个Map,用来装载所有的表单错误信息
* 在校验过程中,如果失败,向map添加错误信息,其中key为表单字段名称
* 2. 校验之后,查看map长度是否大于0,如果大于0,说明有错误信息,就是有错误!
* > 保存map到request中,保存form到request中,转发到regist.jsp
*
* 3. 如果map为空,说明没有错误信息,向下执行!
*/
// 用来装载所有错误信息
Map<String,String> errors = new HashMap<String,String>();
// 对用户名进行校验
String username = form.getUsername();//获取表单的username
if(username == null || username.trim().isEmpty()) {
errors.put("username", "用户名不能为空!");
} else if(username.length() < 3 || username.length() > 15) {
errors.put("username", "用户名长度必须在3~15之间!");
}
// 对密码进行校验
String password = form.getPassword();
if(password == null || password.trim().isEmpty()) {
errors.put("password", "密码不能为空!");
} else if(password.length() < 3 || password.length() > 15) {
errors.put("password", "密码长度必须在3~15之间!");
}
// 对验证码进行校验
String sessionVerifyCode = (String) request.getSession().getAttribute("session_vcode");
String verifyCode = form.getVerifyCode();
if(verifyCode == null || verifyCode.trim().isEmpty()) {
errors.put("verifyCode", "验证码不能为空!");
} else if(verifyCode.length() != 4) {
errors.put("verifyCode", "验证码长度必须为4!");
} else if(!verifyCode.equalsIgnoreCase(sessionVerifyCode)) {
errors.put("verifyCode", "验证码错误!");
}
/*
* 判断map是否为空,不为空,说明存在错误
*/
if(errors != null && errors.size() > 0) {
/*
* 1. 保存errors到request域
* 2. 保存form到request域,为了回显
* 3. 转发到regist.jsp
*/
request.setAttribute("errors", errors);
request.setAttribute("user", form);
request.getRequestDispatcher("/user/regist.jsp").forward(request, response);
return;
}
/*
* 2. 调用userService的regist()方法,传递form过去
* 3. 得到异常:获取异常信息,保存到request域,转发到regist.jsp中显示
* 4. 没有异常:输出注册成功!
*/
try {
userService.regist(form);
response.getWriter().print("<h1>注册成功!</h1><a href='" +
request.getContextPath() +
"/user/login.jsp" + "'>点击这里去登录</a>");
} catch (UserException e) {
// 获取异常信息,保存到request域
request.setAttribute("msg", e.getMessage());
// 还要保存表单数据,到request域
request.setAttribute("user", form);//用来在表单中回显!
// 转发到regist.jsp
request.getRequestDispatcher("/user/regist.jsp").forward(request, response);
}
}
}
对于自定义异常,只需要继承exception并给出父类构造器即可。
package cn.itcast.user.service;
/**
* 自定义一个异常类
* 只是给出父类中的构造器即可!方便用来创建对象!
* @author cxf
*
*/
public class UserException extends Exception {
public UserException() {
super();
// TODO Auto-generated constructor stub
}
public UserException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public UserException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public UserException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
对于登陆时数据处理Servlet,有这么几件事要干:
1、利用toBean将登陆的账号密码保存到表单中
2、调用service的登录功能,如果成功登陆,就将user信息保存到session域,不成功就抛出异常信息
3、成功再转发到welcome页面
package cn.itcast.user.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.commons.CommonUtils;
import cn.itcast.user.domain.User;
import cn.itcast.user.service.UserException;
import cn.itcast.user.service.UserService;
/**
* UserServlet层
* @author cxf
*
*/
public class LoginServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");//请求编码(POST)
response.setContentType("text/html;charset=utf-8");//响应编码
// 依赖UserService
UserService userService = new UserService();
/*
* 1. 封装表单数据到User form中
* 2. 调用service的login()方法,得到返回的User user对象。
* > 如果抛出异常:获取异常信息,保存到request域,再保存form,转发到login.jsp
* > 如果没有异常:保存返回值到session中,重定向到welcome.jsp
*/
User form = CommonUtils.toBean(request.getParameterMap(), User.class);
try {
User user = userService.login(form);
request.getSession().setAttribute("sessionUser", user);
response.sendRedirect(request.getContextPath() + "/user/welcome.jsp");
} catch(UserException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("user", form);
request.getRequestDispatcher("/user/login.jsp").forward(request, response);
}
}
}
接下来在还要做一个生成验证码的servlet
调用VorifyCode类中的生成图片的方法并保存在bufferedImage中,我们还需要保存每一个验证码的文字到session域中,最后输出图片到客户端
package cn.itcast.user.web.servlet;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.vcode.utils.VerifyCode;
public class VerifyCodeServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 创建验证码类
*/
VerifyCode vc = new VerifyCode();
/*
* 2. 得到验证码图片
*/
BufferedImage image = vc.getImage();
/*
* 3. 把图片上的文本保存到session中
*/
request.getSession().setAttribute("session_vcode", vc.getText());
/*
* 4. 把图片响应给客户端
*/
VerifyCode.output(image, response.getOutputStream());
}
}
好的一个简单的用户注册登陆功能就基本实现了
错误分析:
1、验证码地方一直出不来图片,搜寻不到VerifyCodeServlet。最后发现是web.xml没有添加servlet路径
<servlet>
<servlet-name>VerifyCodeServlet</servlet-name>
<servlet-class>user.web.servlet.VerifyCodeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>VerifyCodeServlet</servlet-name>
<url-pattern>/VerifyCodeServlet</url-pattern>
</servlet-mapping>
2、空指针异常
java.lang.NullPointerException
user.web.servlet.RegistServlet.doPost(RegistServlet.java:30)
javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
发现是username去空格以后,我对空名字长度进行了length()方法的调用