注册功能
上图为注册功能的简单实现图,步骤的简单解释和笔者自己的理解如下:
前端页面需要完成的逻辑。
第一步:使用js完成表单校验;
第二步:使用ajax完成表单提交;
提交过后请求会发送到后端RegistUserServlet。
第三步:后端获取前端传来的数据;
第四步:将数据通过工具封装成User对象;
之后调用service的方法,所以需要先编写service。service还要操作数据库,所以在编写service之前,要先编写dao。根据需求我们应该先查找有没有注册过的账号,如果有返回注册失败,反之进行注册。
第五步:编写根据Username查找User的SQL并封装成方法;
第六步:编写保存User的SQL并封装成方法;
第七步:调用dao层的方法,完成service的逻辑;
第八步:更具service的返回,提示消息,返回前端;
第九步:编写前端ajax请求返回的逻辑;
完成注册功能之后,我们添加二维码校验的相应逻辑。
第十步:在RegistUserServlet中添加二维码的校验。
使用JS完成表单校验
/*
表单校验:
1.用户名:单词字符,长度8到20位
2.密码:单词字符,长度8到20位
3.email:邮件格式
4.姓名:非空
5.手机号:手机号格式
6.出生日期:非空
7.验证码:非空
*/
//校验用户名
//单词字符,长度8到20位
function checkUsername() {
//1.获取用户名值
var username = $("#username").val();
//2.定义正则
var reg_username = /^w{8,20}$/;
//3.判断,给出提示信息
var flag = reg_username.test(username);
if (flag) {
//用户名合法
$("#username").css("border", "");
} else {
//用户名非法,加一个红色边框
$("#username").css("border", "1px solid red");
}
return flag;
}
//校验密码
function checkPassword() {
//1.获取密码值
var password = $("#password").val();
//2.定义正则
var reg_password = /^w{6,20}$/;
//3.判断,给出提示信息
var flag = reg_password.test(password);
if (flag) {
//密码合法
$("#password").css("border", "");
} else {
//密码非法,加一个红色边框
$("#password").css("border", "1px solid red");
}
return flag;
}
//校验邮箱
function checkEmail() {
//1.获取邮箱
var email = $("#email").val();
//2.定义正则 itcast@163.com
var reg_email = /^w+@w+.w+$/;
//3.判断
var flag = reg_email.test(email);
if (flag) {
$("#email").css("border", "");
} else {
$("#email").css("border", "1px solid red");
}
return flag;
}
//姓名校验
function checkNickname() {
//1.获取邮箱
var name = $("#name").val();
//3.判断
var flag = name != null;
if (name == null && name == '') {
$("#name").css("border", "1px solid red");
} else {
$("#name").css("border", "");
}
return flag;
}
//姓名校验
function checkNickname() {
//1.获取邮箱
var name = $("#name").val();
var flag = false;
//2.判断
if (name == null || name == '') {
$("#name").css("border", "1px solid red");
} else {
flag = true;
$("#name").css("border", "");
}
return flag;
}
//升级号码校验
function checkTelephone() {
//1.获取邮箱
var telephone = $("#telephone").val();
//2.定义正则 itcast@163.com
var reg_telephone = /^1[3-9]d{9}$/;
//3.判断
var flag = reg_telephone.test(telephone);
if (flag) {
$("#telephone").css("border", "");
} else {
$("#telephone").css("border", "1px solid red");
}
return flag;
}
//出生日期校验
function checkBirthday() {
//1.获取邮箱
var birthday = $("#birthday").val();
var flag = false;
//2.判断
if (birthday == null || birthday == '') {
$("#birthday").css("border", "1px solid red");
} else {
flag = true;
$("#birthday").css("border", "");
}
return flag;
}
//验证码校验
function checkCode() {
//1.获取邮箱
var check = $("#check").val();
var flag = false;
//2.判断
if (check == null || check == '') {
$("#check").css("border", "1px solid red");
} else {
flag = true;
$("#check").css("border", "");
}
return flag;
}
视屏中老师没有全部完成,笔者自己填充了。都是很简单的js语法,不做过多解释。下面的代码时提交的时候触发的代码。整合了上面所有的校验方法。
$(function () {
//当表单提交时,调用所有的校验方法
$("#registerForm").submit(function () {
//1.发送数据到服务器
if (checkUsername() && checkPassword() && checkEmail() && checkNickname()
&& checkTelephone() && checkBirthday() && checkCode()) {
//请求逻辑
}
//2.不让页面跳转
return false;
//如果这个方法没有返回值,或者返回为true,则表单提交,如果返回为false,则表单不提交
});
//当某一个组件失去焦点是,调用对应的校验方法
$("#username").blur(checkUsername);
$("#password").blur(checkPassword);
$("#email").blur(checkEmail);
$("#name").blur(checkNickname);
$("#telephone").blur(checkTelephone);
$("#birthday").blur(checkBirthday);
$("#check").blur(checkCode);
});
异步(ajax)提交表单
在此使用异步提交表单是为了获取服务器响应的数据。因为我们前台使用的是html作为视图层,不能够直接从servlet相关的域对象获取值,只能通过ajax获取响应数据。
$.post("registUserServlet", $(this).serialize(), function (data) {
});
ajax异步请求有三个参数,第一个是请求的路径,第二个是表单的参数的序列化,可以将表单的参数转化成username=zhangsan&password=123的形式,第三个是回调方法,其中data就是返回的数据。
后端获取前端传来的数据
Map<String, String[]> map = request.getParameterMap();
很简单直接从request中拿就行了。
将数据通过工具封装成User对象
//2.封装对象
User user = new User();
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
使用了BeanUtils(org.apache.commons.beanutils),将表单传入的参数和实体类一一对应的转化,所以要求传入的参数和实体属性名称一致。
根据Username查找User
@Override
public User findByUsername(String username) {
User user = null;
try {
// 1. 定义sql
String sql = "SELECT * FROM tab_user WHERE username = ?";
// 2. 执行Sql
user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), username);
} catch (Exception e) {
}
return user;
}
使用了JdbcTemplate工具,执行SQL语句,很简单没什么可说的。
保存User
@Override
public void saveUser(User user) {
// 1. 定义sql
String sql = "INSERT INTO tab_user(username, password, name, birthday, sex, telephone, email) VALUES (?,?,?,?,?,?,?) ";
// 2. 执行sql
template.update(sql, user.getUsername(), user.getPassword(), user.getName(),
user.getBirthday(), user.getSex(), user.getTelephone(), user.getEmail());
}
完成service的逻辑
@Override
public Boolean regist(User user) {
//1. 根据用户名查询用户对象(如果存在返回false)
User u = userDAO.findByUsername(user.getUsername());
//判断u是否为null
if (u != null) {
//用户名存在,注册失败
return false;
}
//2. 保存用户信息
userDAO.saveUser(user);
return true;
}
处理service的返回
ResultInfo result = new ResultInfo();
//4.相应结果
if (flag) {
//注册成功
result.setFlag(true);
} else {
//注册失败
result.setFlag(false);
result.setErrorMsg("注册失败!");
}
//将result对象序列化为json
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
//将json写会输出端
//设置content-text
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
ResultInfo是一个返回的格式类,格式化成json之后,直接返回。
前端ajax请求返回处理
if (data.flag) {
//注册成功,跳转成功页面
location.href = "register_ok.html";
} else {
//注册失败,给errorMsg添加提示信息
$("#errorMsg").html(data.errorMsg);
}
成功跳转,失败报错。
二维码校验
//验证码校验
String check = request.getParameter("check");
//从session中获取验证码
HttpSession session = request.getSession();
String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
//为了保证验证码只能使用一次
session.removeAttribute("CHECKCODE_SERVER");
//不区分大小写的比较
if (checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)) {
//验证码错误
ResultInfo result = new ResultInfo();
result.setFlag(false);
result.setErrorMsg("验证码错误");
//将result对象序列化为json
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return;
}
从表单参数中获取用户输入的验证码check,再从session中获取正确的二维码。进行不区分大小写的比较,如果不对直接返回,正确进行接下来的注册任务。 为了保证验证码只能使用一次,每取出一个验证码,就会清空。
注册功能:发送邮件
为什么要进行邮件激活?为了保证用户填写的邮箱是正确的。将来可以推广一些宣传信到用户邮箱中。
//3. 激活邮件发送
//如果是发布的项目就不能使用localhost,而是项目的域名
String content = "<a herf='http://localhost/travel/activeUserServlet?code=" + user.getCode() + "'>激活黑马旅游网</a>";
MailUtils.sendMail(user.getEmail(), content, "激活邮件");
我们在UserServiceImpl.regist()中添加发送邮件的功能,用来实现在注册账户之后发送邮件激活。这里使用MailUtils.sendMail。
需要注意三点
- 发送邮件的内容是一段超链接,请求就是激活账户的servlet,并且携带唯一的激活码作为参数;
- MailUtils前,需要配置账号密码,替换XXX;
private static final String USER = "XXX"; // 发件人称号,同邮箱地址
private static final String PASSWORD = "XXX"; // 如果是qq邮箱可以使户端授权码,或者登录密码
- 邮箱需要开通POP3/SMTP服务,QQ邮箱和163邮箱都一样。
注册功能:用户点击邮件激活
上图为用户点击邮件激活功能的简单实现图,步骤的简单解释和笔者自己的理解如下:
第一步:通过MailUtils发送一段超链接,所以邮件内容为一个接口地址,点击调用后台servlet;
进入后台逻辑部分。
第二步:先从请求中获取激活码,并判断是否为空;
//1. 获取激活码
String code = request.getParameter("code");
if (code!=null) {
}
之后调用service的方法,所以需要先编写service。service还要操作数据库,所以在编写service之前,要先编写dao。根据需求我们应该先查找有没有该激活码的账户,如果有更改激活状态,反之返回错误信息。
第五步:编写根据code查找User的SQL并封装成方法;
@Override
public User findByCode(String code) {
User user = null;
try {
String sql = "SELECT * FROM tab_user WHERE code = ?";
user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), code);
} catch (DataAccessException e) {
e.fillInStackTrace();
}
return user;
}
第六步:编写更改激活状态的SQL并封装成方法;
@Override
public void modifyStatus(User user) {
String sql = "UPDATE tab_user SET status = 'Y' WHERE uid = ?";
template.update(sql, user.getUid());
}
第七步:调用dao层的方法,完成service的逻辑;
@Override
public boolean active(String code) {
//1. 根据激活码查询用户
User user = userDAO.findByCode(code);
if (user != null) {
// 2. 调用DAO修改激活状态
userDAO.modifyStatus(user);
return true;
}
return false;
}
第八步:根据service的返回,提示消息,返回前端;
//2. 调用service完成激活
UserService service = new UserServiceImpl();
boolean flag = service.active(code);
//3. 判断标志
String msg = null;
if (flag) {
//激活成功
msg = "激活成功,请<a href='login.html'>登录</a>";
}else {
// 激活失败
msg = "激活失败,请联系管理员";
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(msg);
登录功能
上图为注册功能的简单实现图,步骤的简单解释和笔者自己的理解如下:
前端页面逻辑部分
第一步:按钮绑定事件,触发ajax异步请求
进入后端逻辑部分,还是熟悉的套路,servlet->service->dao。所以这次我们先从SQL写起,一步一步向上集成,所以我们需要先熟悉需求的内容,登录就是从前端拿去账号密码,然后进行数据库比对,然后根据结果返回。
第二步:编写校验账号密码的SQL并封装成方法;
第三步:调用dao层的方法,完成service的逻辑;
第四步:调用service层的方法,完成servlet的逻辑;
第五步:返回结果处理。
按钮绑定事件,触发ajax异步请求
$(function () {
//1.给登录按钮绑定单击事件
$("#btn_sub").click(function () {
//2.发送ajax请求,提交表单数据
$.post("loginServlet", $("#loginForm").serialize(), function (data) {
});
});
});
编写校验账号密码的SQL并封装成方法
@Override
public User findByUsernameAndPassword(String username, String password) {
User user = null;
try {
// 1. 定义sql
String sql = "SELECT * FROM tab_user WHERE username = ? AND password = ?";
// 2. 执行Sql
user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), username, password);
} catch (Exception e) {
}
return user;
}
调用dao层的方法,完成service的逻辑
@Override
public User login(User user) {
User u = userDAO.findByUsernameAndPassword(user.getUsername(), user.getPassword());
return u;
}
调用service层的方法,完成servlet的逻辑
//1. 获取用户名和密码数据
Map<String, String[]> map = request.getParameterMap();
//2. 封装User对象
User user = new User();
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//3. 调用service查询
UserService service = new UserServiceImpl();
User u = service.login(user);
ResultInfo result = new ResultInfo();
//4. 判断用户对象是否为null
if (u == null) {
//用户名密码错误
result.setFlag(false);
result.setErrorMsg("用户名或密码错误");
}
//5. 判断用户是否激活
if (u != null && !"Y".equals(u.getStatus())) {
result.setFlag(false);
result.setErrorMsg("您尚未激活,请激活");
}
//6. 判断登录成功
if (u != null && "Y".equals(u.getStatus())) {
//登录成功
result.setFlag(true);
request.getSession().setAttribute("user",u);
}
//响应数据
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json;charset=utf-8");
mapper.writeValue(response.getOutputStream(), result);
这里注意,视频中有一个问题,就是没有写request.getSession().setAttribute("user",u);
,这会导致后面实现index页面中用户姓名的提示信息功能时,无法从session中获取到uesr信息。
返回结果处理
if (data.flag) {
//登录成功
location.href = "index.html";
} else {
//登录失败
$("#errorMsg").html(data.errorMsg);
}
登录功能:index页面中用户姓名的提示信息功能
第一步:header.html中编写动态方法
$(function () {
$.get("findUserServlet", {}, function (data) {
//{uid:1,name:'李四'}
var msg = "欢迎回来," + data.name;
$("#span_username").html(msg);
});
});
第二步:编写FindUserServlet
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//从Session中获取登录用户
Object user = request.getSession().getAttribute("user");
//将user写回客户端
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json;charset=utf-8");
mapper.writeValue(response.getOutputStream(), user);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
从session中获取登陆时存入的信息,然后直接返回。
退出功能
什么叫登录了?session中有user对象。
实现步骤:
- 访问servlet,将session摧毁
//1. 销毁session
request.getSession().invalidate();
- 跳转到登录界面
//2. 跳转登录页面
//重定向需要使用虚拟路径
response.sendRedirect(request.getContextPath() + "/login.html");
- 编写前端界面
<a href="javascript:location.href='exitServlet';">退出</a>