前言:这是一个黑马的项目,主要技术是javaweb中的一些基础的未完全封装的技术,包括servlet,jsp等,写这篇文章的目的就是为了复习这些技术,帮助大家了解一个网页的后台到底是怎么实现的,等这个web基础项目篇完成后,后期可以出框架项目篇。我会写清楚每个地方会应该有的思路,以及放上关键的代码。
相关资料,写完以后会放出来,尽情期待。
如果没有看过相关的基础,请先去学习基础,再来看项目。
本笔记是基于黑马程序员的课程,本人根据视频编写的,如果有侵权,请联系本人进行删除。
另外黑马旅游网的视频链接我放在下面,有需要的请看视频学习:
https://www.bilibili.com/video/av73933644/
注意:学习的源码和相关资料在视频的评论中
一、注册功能
旅游网,采用html方式,不用jsp技术,html加载更加快速,适合客户大量快速访问。
a) 页面初始化会加载所有的静态资源。
b) 在注册页面输入所有的内容会进行表单检验工作。
c) 校验成功以后会跳转到servlet,servlet会调用service中的registerUser方法,进行数据库的查询操作,查看是否存在相同的用户名,如果不存在,执行保存报错。
d) 在service层中查询用户名会调用dao层的findByUesrname和save方法。
e) 最后,在service层返回的保存信息后,会返回一个json格式的提示信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>注册</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" href="css/register.css">
<!--导入jquery-->
<script src="js/jquery-3.3.1.js"></script>
<script>
/*
表单校验:
1.用户名:单词字符,长度8到20位
2.密码:单词字符,长度8到20位
3.email:邮件格式
4.姓名:非空
5.手机号:手机号格式
6.出生日期:非空
7.验证码:非空
*/
//1.校验用户名
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;
}
//2.校验密码
function checkPassword() {
//1.获取密码值
var password = $("#password").val();
//2.定义正则
var reg_password = /^\w{8,20}$/;
//3.判断,给出提示信息
var flag = reg_password.test(password);
if (flag){
//用户名合法
$("#password").css("border","");
} else {
//用户名非法
$("#password").css("border","1px solid red");
}
return flag;
}
//3. 检验邮箱
function checkEmail(){
//1.获取邮箱
var email = $("#email").val();
//2.定义正则 [email protected]
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 () {
//表单提交时,调用所有的校验方法
$("#registerForm").submit(function () {
return checkUsername()&&checkPassword()&&checkEmail();
});
//当某一个组件失去焦点时,调用对应的校验方法
$("#username").blur(checkUsername);
$("#password").blur(checkPassword);
$("#email").blur(checkEmail);
});
</script>
</head>
<body>
<!--引入头部-->
<div id="header"></div>
<!-- 头部 end -->
<div class="rg_layout">
<div class="rg_form clearfix">
<div class="rg_form_left">
<p>新用户注册</p>
<p>USER REGISTER</p>
</div>
<div class="rg_form_center">
<!--注册表单-->
<form id="registerForm" action="user">
<!--提交处理请求的标识符-->
<input type="hidden" name="action" value="register">
<table style="margin-top: 25px;">
<tr>
<td class="td_left">
<label for="username">用户名</label>
</td>
<td class="td_right">
<input type="text" id="username" name="username" placeholder="请输入账号">
</td>
</tr>
<tr>
<td class="td_left">
<label for="password">密码</label>
</td>
<td class="td_right">
<input type="text" id="password" name="password" placeholder="请输入密码">
</td>
</tr>
<tr>
<td class="td_left">
<label for="email">Email</label>
</td>
<td class="td_right">
<input type="text" id="email" name="email" placeholder="请输入Email">
</td>
</tr>
<tr>
<td class="td_left">
<label for="name">姓名</label>
</td>
<td class="td_right">
<input type="text" id="name" name="name" placeholder="请输入真实姓名">
</td>
</tr>
<tr>
<td class="td_left">
<label for="telephone">手机号</label>
</td>
<td class="td_right">
<input type="text" id="telephone" name="telephone" placeholder="请输入您的手机号">
</td>
</tr>
<tr>
<td class="td_left">
<label for="sex">性别</label>
</td>
<td class="td_right gender">
<input type="radio" id="sex" name="sex" value="男" checked> 男
<input type="radio" name="sex" value="女"> 女
</td>
</tr>
<tr>
<td class="td_left">
<label for="birthday">出生日期</label>
</td>
<td class="td_right">
<input type="date" id="birthday" name="birthday" placeholder="年/月/日">
</td>
</tr>
<tr>
<td class="td_left">
<label for="check">验证码</label>
</td>
<td class="td_right check">
<input type="text" id="check" name="check" class="check">
<img src="checkCode" height="32px" alt="" onclick="changeCheckCode(this)">
<script type="text/javascript">
//图片点击事件
function changeCheckCode(img) {
img.src="checkCode?"+new Date().getTime();
}
</script>
</td>
</tr>
<tr>
<td class="td_left">
</td>
<td class="td_right check">
<input type="submit" class="submit" value="注册">
<span id="msg" style="color: red;"></span>
</td>
</tr>
</table>
</form>
</div>
<div class="rg_form_right">
<p>
已有账号?
<a href="#">立即登录</a>
</p>
</div>
</div>
</div>
<!--引入尾部-->
<div id="footer"></div>
<!--导入布局js,共享header和footer-->
<script type="text/javascript" src="js/include.js"></script>
</body>
</html>
//表单提交时,调用所有的校验方法
$("#registerForm").submit(function () {
//1.发送数据
if (checkUsername()&&checkPassword()&&checkEmail()){
//校验通过,发送ajax请求,提交表单数据 username=zhangsan&password=123
$.post("registerUserServlet",$(this).serialize(),function (data) {
//处理服务器响应的数据 data
});
}
//2.跳转页面
return false;
});
知识点补充:将表单内容序列化成字符串,使用$().serialize(),会转换成key=value的形式,比如说username=zhangsan&password=123
servlet是与前端交互最为接近的模块,可以返回浏览器的数据。
RegisterUserServlet接收并封装request的请求中的数据到user对象中,user可以作为service层的参数,接收返回是否注册成功的信息。
如果成功,将成功信息写入resultInfo对象中,否则写入失败信息。
最后用jackson序列化结果信息,给浏览器返回json数据,不要忘了返回时加上content-type。
RegisterUserServlet.java
package cn.itcast.travel.web.servlet;
import cn.itcast.travel.domain.ResultInfo;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;
import cn.itcast.travel.service.impl.UserServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet(name = "registerUserServlet")
public class RegisterUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取数据
Map<String, String[]> map = request.getParameterMap();
//2.封装对象
User user = new User();
try {
BeanUtils.populate(user,map);
} catch (Exception e) {
e.printStackTrace();
}
//3.调用service完成注册
UserService userService = new UserServiceImpl();
boolean flag = userService.regist(user);
ResultInfo info = new ResultInfo();
//4.响应结果
if (flag){
//注册成功
info.setFlag(true);
} else {
//注册失败
info.setFlag(false);
info.setErrorMsg("注册失败!");
}
//将info对象序列化为json
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
//将json数据写回客户端,字符流写回
//设置content-type
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
UserDao.java
package cn.itcast.travel.dao;
import cn.itcast.travel.domain.User;
public interface UserDao {
/**
* 根据用户名查询用户信息
* @param username
* @return
*/
public User findByUsername(String username);
/**
* 用户保存
* @param user
*/
public void save(User user);
}
UserDaoImpl.java
package cn.itcast.travel.dao.impl;
import cn.itcast.travel.dao.UserDao;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class UserDaoImpl implements UserDao {
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@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) {
//出现异常不做处理,user会为null
}
return user;
}
@Override
public void save(User user) {
String sql = "insert into tab_user(username,password,name,birthday,sex,telephone,email,status,code) values(?,?,?,?,?,?,?,?,?)";
template.update(sql,
user.getUsername(),
user.getPassword(),
user.getName(),
user.getBirthday(),
user.getSex(),
user.getTelephone(),
user.getEmail(),
user.getStatus(),
user.getCode()
);
}
}
用户注册service层目前要实现以下两个内容:
1.根据用户名查询用户对象find
2.保存用户信息save
完成最简单的数据库交互的代码,后期再加入验证码校验等功能模块
完善相关的代码,直到调试出数据库出现内容即可
UserService.java
package cn.itcast.travel.service;
import cn.itcast.travel.domain.User;
import javax.print.attribute.standard.RequestingUserName;
public interface UserService {
/**
* 注册用户
* @param user
* @return
*/
boolean regist(User user);
}
UserServiceImpl.java
package cn.itcast.travel.service.impl;
import cn.itcast.travel.dao.UserDao;
import cn.itcast.travel.dao.impl.UserDaoImpl;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
/**
* 注册用户
* @param user
* @return
*/
@Override
public boolean regist(User user) {
//1.根据用户名查询用户对象
User u = userDao.findByUsername(user.getUsername());
//判断u是否为null
if (u != null){
//用户名存在,注册失败
return false;
}
//2.保存用户信息
userDao.save(user);
return true;
}
}
为了防止验证码复用,不会生成新的验证码,所以获取一次session中的验证码后必须要清除。
后台-servlet完善
//验证校验成功后再执行后续操作
String check = request.getParameter("check");
//从session中获取验证码
HttpSession session = request.getSession();
String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
//比较
if (!checkcode_server.equalsIgnoreCase(check)){
//验证码错误
ResultInfo info = new ResultInfo();
//注册失败
info.setFlag(false);
info.setErrorMsg("验证码错误");
//将info对象序列化为json
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return;
}
前台-html完善
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>注册</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" href="css/register.css">
<!--导入jquery-->
<script src="js/jquery-3.3.1.js"></script>
<script>
/*
表单校验:
1.用户名:单词字符,长度8到20位
2.密码:单词字符,长度8到20位
3.email:邮件格式
4.姓名:非空
5.手机号:手机号格式
6.出生日期:非空
7.验证码:非空
*/
//1.校验用户名
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;
}
//2.校验密码
function checkPassword() {
//1.获取密码值
var password = $("#password").val();
//2.定义正则
var reg_password = /^\w{8,20}$/;
//3.判断,给出提示信息
var flag = reg_password.test(password);
if (flag){
//用户名合法
$("#password").css("border","");
} else {
//用户名非法
$("#password").css("border","1px solid red");
}
return flag;
}
//3. 检验邮箱
function checkEmail(){
//1.获取邮箱
var email = $("#email").val();
//2.定义正则 [email protected]
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 () {//页面加载时
//表单提交时,调用所有的校验方法
$("#registerForm").submit(function () {
//1.发送数据
if (checkUsername()&&checkPassword()&&checkEmail()){
//校验通过,发送ajax请求,提交表单数据
$.post("registUserServlet",$(this).serialize(),function (data) {
//处理服务器响应的数据 data {flag:flase,errorMsg:"注册失败"}
if (data.flag){
//注册成功,跳转成功页面
location.href="register_ok.html";
} else {
//注册失败,给errorMsg添加提示信息
$("#errorMsg").html(data.errorMsg);
}
});
}
//2.跳转页面
return false;
});
//当某一个组件失去焦点时,调用对应的校验方法
$("#username").blur(checkUsername);
$("#password").blur(checkPassword);
$("#email").blur(checkEmail);
});
</script>
</head>
<body>
<!--引入头部-->
<div id="header"></div>
<!-- 头部 end -->
<div class="rg_layout">
<div class="rg_form clearfix">
<div class="rg_form_left">
<p>新用户注册</p>
<p>USER REGISTER</p>
</div>
<div class="rg_form_center">
<div id="errorMsg" style="color: red;text-align: center"></div>
<!--注册表单-->
<form id="registerForm" action="user">
<!--提交处理请求的标识符-->
<input type="hidden" name="action" value="register">
<table style="margin-top: 25px;">
<tr>
<td class="td_left">
<label for="username">用户名</label>
</td>
<td class="td_right">
<input type="text" id="username" name="username" placeholder="请输入账号">
</td>
</tr>
<tr>
<td class="td_left">
<label for="password">密码</label>
</td>
<td class="td_right">
<input type="text" id="password" name="password" placeholder="请输入密码">
</td>
</tr>
<tr>
<td class="td_left">
<label for="email">Email</label>
</td>
<td class="td_right">
<input type="text" id="email" name="email" placeholder="请输入Email">
</td>
</tr>
<tr>
<td class="td_left">
<label for="name">姓名</label>
</td>
<td class="td_right">
<input type="text" id="name" name="name" placeholder="请输入真实姓名">
</td>
</tr>
<tr>
<td class="td_left">
<label for="telephone">手机号</label>
</td>
<td class="td_right">
<input type="text" id="telephone" name="telephone" placeholder="请输入您的手机号">
</td>
</tr>
<tr>
<td class="td_left">
<label for="sex">性别</label>
</td>
<td class="td_right gender">
<input type="radio" id="sex" name="sex" value="男" checked> 男
<input type="radio" name="sex" value="女"> 女
</td>
</tr>
<tr>
<td class="td_left">
<label for="birthday">出生日期</label>
</td>
<td class="td_right">
<input type="date" id="birthday" name="birthday" placeholder="年/月/日">
</td>
</tr>
<tr>
<td class="td_left">
<label for="check">验证码</label>
</td>
<td class="td_right check">
<input type="text" id="check" name="check" class="check">
<img src="checkCode" height="32px" alt="" onclick="changeCheckCode(this)">
<script type="text/javascript">
//图片点击事件
function changeCheckCode(img) {
img.src="checkCode?"+new Date().getTime();
}
</script>
</td>
</tr>
<tr>
<td class="td_left">
</td>
<td class="td_right check">
<input type="submit" class="submit" value="注册">
<span id="msg" style="color: red;"></span>
</td>
</tr>
</table>
</form>
</div>
<div class="rg_form_right">
<p>
已有账号?
<a href="#">立即登录</a>
</p>
</div>
</div>
</div>
<!--引入尾部-->
<div id="footer"></div>
<!--导入布局js,共享header和footer-->
<script type="text/javascript" src="js/include.js"></script>
</body>
</html>
最后检验一下不输入验证码以及用户名重复的信息提示信息是否解决。
发送邮件
邮件的Utils不需要会写,需要会改,现在给邮箱开通的能发送的服务
1.给邮箱开通POP3/SMTP服务,我用的qq邮箱
官方教程地址:https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256
2.开通后完善工具类中相关邮件授权码部分的填写,并测试发送邮件的工具类,收到邮件则说明成功
点击邮件链接激活
邮件注册分析
激活就是将数据库中状态N改成Y
修改注册业务的代码,加入code激活码和status状态,并发送邮件
package cn.itcast.travel.service.impl;
import cn.itcast.travel.dao.UserDao;
import cn.itcast.travel.dao.impl.UserDaoImpl;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;
import cn.itcast.travel.util.MailUtils;
import cn.itcast.travel.util.UuidUtil;
import java.util.UUID;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
/**
* 注册用户
* @param user
* @return
*/
@Override
public boolean regist(User user) {
//1.根据用户名查询用户对象
User u = userDao.findByUsername(user.getUsername());
//判断u是否为null
if (u != null){
//用户名存在,注册失败
return false;
}
//2.保存用户信息
//2.1 设置激活码,唯一字符串
user.setCode(UuidUtil.getUuid());
//2.2 设置激活状态
user.setStatus("N");
userDao.save(user);
//3.激活邮件发送,邮件正文
String content = "点击激活黑马旅游网";
MailUtils.sendMail(user.getEmail(),content,"激活邮件");
return true;
}
}
邮件状态是个超链接,并点击
收到邮件,并点击查看地址栏信息,需要我们添加状态修改的激活servlet (图片是我写好后的效果)
a)servlet层
package cn.itcast.travel.web.servlet;
import cn.itcast.travel.service.UserService;
import cn.itcast.travel.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/activeUserServlet")
public class ActiveUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取激活码
String code = request.getParameter("code");
if (code != null){
//2.调用service完成激活
UserService userService = new UserServiceImpl();
boolean flag = userService.active(code);
//3.判断标记
String msg = null;
if (flag){
//激活成功
msg = "激活成功,请登录";
} else {
//激活失败
msg = "激活失败,请联系管理员!";
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(msg);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
b)service层
接口
/**
* 用户激活
* @param code
* @return
*/
@Override
public boolean active(String code) {
//1.根据激活码查询用户对象
User user = userDao.findByCode();
if (user != null){
//2.调用dao的修改激活状态的方法
userDao.updateStatus(user);
return true;
} else {
return false;
}
}
实现
/**
* 用户激活
* @param code
* @return
*/
@Override
public boolean active(String code) {
//1.根据激活码查询用户对象
User user = userDao.findByCode(code);
if (user != null){
//2.调用dao的修改激活状态的方法
userDao.updateStatus(user);
return true;
} else {
return false;
}
}
c)dao层
接口
/**
* 根据激活码查询用户对象
* @return
*/
User findByCode(String code);
/**
* 修改指定用户状态
* @param user
*/
void updateStatus(User user);
实现
/**
* 根据激活码查询用户对象
* @return
*/
@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 (Exception e) {
//出现异常则不封装对象,user中为空
e.printStackTrace();
}
return user;
}
/**
* 修改指定用户状态
* @param user
*/
@Override
public void updateStatus(User user) {
String sql = "update tab_user set status = 'Y' where uid =?";
template.update(sql,user.getUid());
}
最后,查看激活链接网页的跳转和数据库中的数据是否正常修改