项目需求分析
(1)用户注册与登录模块:未注册用户可以通过注册用户信息,登录到资源交流平台,达到相互交流学习的目的。已有账号密码的学生或老师可以直接登录课程资源库系统,进行资源浏览。
- 学生用户成功登陆Web资源库后可以上传学生资源,收藏和下载系统内所有资源,发布学生帖子和评论所有帖子,关注其他用户。
- 教师用户成功登陆Web资源库后可以上传教师资源,收藏和下载系统内所有资源,发布教师帖子和评论所有帖子,关注其他用户。
- 管理员用户具有用户信息管理、学生信息批量导入、资源管理、交流区管理的权限。
(2)资源浏览与下载模块:用户可以直接在web资源的首页查看到所有发布的文件和图片,对于共享的资源文件可以直接下载保存。 (3)资源上传模块:用户可以直接上传资源文件,上传文件时需要录入资源的名字,类别,等级和描述。 (4)学习推荐模块:随着Web资源库的内容增加,用户需要从大量资源文件中获取自己想要的资源。学习推荐模块,是对用户实施的一个推荐功能。用户在推荐的目录文件中也可以选择关注自己感兴趣的用户,一起参与讨论。 (5)用户交流区模块:在论坛中,学生用户可以直接发贴提问,回复和评论所有帖子,教师用户可以发帖回答问题,回复和评论所有帖子,帮助学生解决学习上的疑惑。 (6)后台管理模块:管理员进行Web资源库的后台管理。管理员可以在此模块管理用户账号,进行增删查改的操作,并且批量导入学生信息。同时也要讲系统内的资源构件分门别类,删除出错的构件,将优质资源标“优”,将优质的资源摘录到Web资源库首页并且置顶,将交流区的优质回复摘录到Web资源库首页置顶的用户评论区,对交流区内违规操作的用户进行禁言处理。
概要设计
经过需求分析后,按照系统实现的功能进行模块划分,将相关功能的实现划分到同一模块中。 (1)按照管理系统的要求将模块划分为六大部分,用户注册与登录模块,资源浏览与下载模块,资源上传模块和推荐学习模块,用户交流区模块,后台管理模块。
(2)将六个模块细化
| |
---|
用户注册与登录-注册 | 注册成为资源库的一个用户,可以共享web资源内容 | 用户注册与登录-登录 | 学生用户、教师用户和管理员用户登录账号,分别拥有不同的权限 | 后台管理-用户管理 | 对用户信息进行管理,批量导入学生,增删查改 | 后台管理-资源管理 | 对资源信息进行管理,删除出错的构件,将优质资源标“优” | 后台管理-论坛管理 | 对发布的帖子进行管理,对交流区内违规操作的用户进行禁言处理 | 资源浏览与下载-资源中心 | 包含所有用户已经上传的资源信息,可以让用户浏览 | 资源浏览与下载-资源下载 | 将感兴趣的内容进行资源下载保存到本地 | 资源上传-上传 | 教师可以上传教师资源文件,学生可以上传个人文件 | 推荐学习-资源置顶推荐 | 系统将推荐的资源置顶,让用户方便搜索到想要的内容 | 用户交流区-论坛帖子 | 汇总了所有学生和教师的帖子,方便大家在论坛内展开讨论 | 用户交流区-我要发帖 | 学生和教师可以发布个人的帖子,评论他人的帖子,关注其他用户 |
三层架构搭建项目结构
三层架构简介
首先来说,三层架构与MVC的目标一致:都是为了解耦和、提高代码复用。MVC是一种设计模式,而三层架构是一种软件架构。
三层架构分为:控制层(controller层即servlet)、业务逻辑层(service层)、数据访问层(dao层) ,再加上实体类(Model)和表现层(web层即jsp)
实体类(Model):
在Java中,往往将其称为Entity实体类。数据库中用于存放数据,而我们通常选择会用一个专门的类来抽象出数据表的结构,类的属性就一对一的对应这表的属性。
Model实体类需要被dao层,service层和web层引用。
数据访问层(dao):
主要是存放对数据类的访问,即对数据库的添加、删除、修改、更新等基本操作
dao就是根据业务需求,构造SQL语句,构造参数,调用帮助类,获取结果,dao层被service层调用
业务逻辑层(service)
service层好比是桥梁,将web表示层与dao数据访问层之间联系起来。所要负责的,就是处理涉及业务逻辑相关的问题,比如在调用访问数据库之前,先处理数据、判断数据。
后端注册功能实现
eclipse配置
双击eclipse,选择自己的工作空间
设置工作空间的字符编码
设置JSP的字符编码
配置tomcat服务器
创建web项目
按Ctrl+N输入dynamic web project
用户表设计与创建数据库和表
用户表设计
学生,教师都有个人信息(姓名,性别,年龄,生日),但教师不包含班级字段,但它们都有账号信息以及身份标识。管理员只有账号信息,身份标识。
注册时一般只需提供账号,密码,身份标识,如果是学生还需要提供班级,而个人信息是通过登录后通过个人信息修改的。
为了简单起见,可以把学生,教师,管理员放在一张表中,共有的字段设置为不可为空(账号,密码,身份标识),不共有的设置可以为空,表如下
user表
字段名称 | 类型 | 约束 | 描述 |
---|
user_id | varchar(20) | NOT NULL PRIMARY KEY | 用户ID | user_password | varchar(20) | NOT NULL | 用户密码 | user_name | varchar(20) | NULL | 用户姓名(默认为空) | user_sex | varchar(10) | NULL | 用户性别(默认为空) | user_birthday | date() | NULL | 用户生日(默认为空) | user_class | varchar(20) | NULL | 班级 | user_level | varchar(20) | NOT NULL | 用户标识 |
使用mysql命令行创建数据库和表
CREATE DATABASE web_resource;
CREATE TABLE `user` (
`user_id` varchar(20) NOT NULL,
`user_password` varchar(20) NOT NULL,
`user_name` varchar(20) NULL,
`user_sex` varchar(10) DEFAULT NULL,
`user_birthday` date DEFAULT NULL,
`user_class` varchar(20) DEFAULT NULL,
`user_level` varchar(20) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
创建用户实体类
鼠标左键java文件夹按Ctrl+N输入class
输入存放实体类的包名和类名后finish
user.java代码如下
public class User {
private String userId;
private String userPassword;
private String userName;
private String userSex;
private String userBirthday;
private String userClass;
private String userLevel;
public User() {
super();
}
public User(String userId, String userPassword, String userName, String userSex, String userBirthday,
String userClass, String userLevel) {
super();
this.userId = userId;
this.userPassword = userPassword;
this.userName = userName;
this.userSex = userSex;
this.userBirthday = userBirthday;
this.userClass = userClass;
this.userLevel = userLevel;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(String userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserClass() {
return userClass;
}
public void setUserClass(String userClass) {
this.userClass = userClass;
}
public String getUserLevel() {
return userLevel;
}
public void setUserLevel(String userLevel) {
this.userLevel = userLevel;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userPassword=" + userPassword + ", userName=" + userName + ", userSex="
+ userSex + ", userBirthday=" + userBirthday + ", userClass=" + userClass + ", userLevel=" + userLevel
+ "]";
}
}
控制层(controller)实现
鼠标左键java文件夹按Ctrl+N输入servlet
输入存放servlet的包名(com.zhuo.controller),由于注册是和用户相关的,还可以建一个子目录user,这样可以和后面各种类型的servlet进行区分,然后输入类名后next
修改URL映射,这样浏览器可以通过地址栏访问注册的servlet
RegisterServlet.java代码如下
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String userId = request.getParameter("userId");
String userPassword = request.getParameter("userPassword");
String userName = request.getParameter("userName");
String userLevel = request.getParameter("userLevel");
String userClass = request.getParameter("userClass");
User user = new User();
user.setUserId(userId);
user.setUserPassword(userPassword);
user.setUserName(userName);
user.setUserLevel(userLevel);
user.setUserClass(userClass);
UserServiceImpl userServiceImpl = new UserServiceImpl();
int i = 0;
try {
i = userServiceImpl.register(user);
} catch (SQLException e) {
e.printStackTrace();
}
if (i > 0) {
response.sendRedirect("views/before/login.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
RegisterServlet的主要作用就是接受客户提交的表单(通过注册页面提交),然后把参数封装到实体类中,之后就可以以实体类作用参数调用service层业务逻辑处理方法,通过返回值判断是否注册成功
业务逻辑层(service)实现
service层分为接口和接口的实现类,为了要定义接口呢?直接用类不行吗?
我们知道接口是空实现,具体实现由子类继承。那么这样的好处就是当我们扩展业务功能时,只需在接口添加即可,具体的实现交给子类实现。由于接口只包含方法签名,而不包含复杂的逻辑代码,很容易就可以看出有什么业务功能
定义接口
鼠标左键java文件夹按Ctrl+N输入interface
输入存放service层的接口包名和接口名后finish
在UserService.java中添加一个注册的抽象方法
public int register(User user) throws SQLException;
定义接口的实现类
鼠标左键java文件夹按Ctrl+N输入class
输入存放service层的接口实现类的包名和类名并继承UserService接口后finish
在UserServiceImpl实现类中重写接口的register方法
UserDao userDao = new UserDaoImpl();
@Override
public int register(User user) throws SQLException {
int i = userDao.insertUser(user);
return i;
}
实现类中重写的register方法,主要功能是封装了底层操作(即对数据库的增删改查),只提现业务功能(即注册)
数据访问层(dao)实现
dao层和service层类似,就不再重复阐述了
定义接口
鼠标左键java文件夹按Ctrl+N输入interface
输入存放dao层接口的包名(com.zhuo.dao)和类名后finish
在UserDao.java中添加一个插入用户到数据库抽象方法
public int insertUser(User user) throws SQLException;
定义接口的实现类
输入存放dao层的接口实现类的包名和类名并继承UserDao接口后finish
在UserDaoImpl实现类中重写接口的insertUser方法
ResultSet resultSet;
@Override
public int insertUser(User user) throws SQLException {
String sql = "insert into user (user_id, user_password, user_name, "
+ "user_sex, user_birthday, user_class, user_level) "
+ "values(?,?,?,?,?,?,?);";
int i = JDBCUtil.executeUpdate(sql, user.getUserId(),
user.getUserPassword(), user.getUserName(),
user.getUserSex(), user.getUserBirthday(),
user.getUserClass(), user.getUserLevel());
return i;
}
实现类UserDaoImpl重写了接口的insertUser方法,主要功能是调用jdbc工具执行sql插入语句添加用户
使用jdbc工具类和导入mysql驱动包
创建jdbc工具类
鼠标左键java文件夹按Ctrl+N输入class
输入存放工具类的包名和类名后finish
JDBCUtil.java代码如下
public class JDBCUtil {
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/web_resource?useUnicode=true&characterEncoding=utf-8";
private static final String USER = "root";
private static final String PASSWORD = "12345";
private static Connection ct;
private static PreparedStatement ps;
private static ResultSet rs;
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
ct = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return ct;
}
public static ResultSet executeQuery(String sql, Object... obj) {
ct = getConnection();
try {
ps = ct.prepareStatement(sql);
if (obj != null) {
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
public static int executeUpdate(String sql, Object... obj) {
ct = getConnection();
try {
ps = ct.prepareStatement(sql);
if (obj != null) {
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
}
int in = ps.executeUpdate();
close(ct, ps, null);
return in;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
public static void close(Connection ct, PreparedStatement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ct != null) {
try {
ct.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static Connection getCt() {
return ct;
}
public static PreparedStatement getPs() {
return ps;
}
}
JDBCUtil工具类只需把常量PASSWORD修改为自己的mysql密码即可
导入mysql驱动包
要想连接数据库,需要加载mysql驱动包
jar包下载地址:https://repo1.maven.org/maven2/mysql/mysql-connector-java/
选择对应自己的mysql版本,下载好之后复制到WEB-INF的lib文件夹即可
至此后台主要的功能都实现了,等到编写前台代码时会加些前台表单验证的servlet(验证码,检查用户是否存在)。
到目前为止的项目结构如下
前端注册功能实现
前端页面包含结构(html,jsp),样式(css即表现),行为(javascript)。
Bootstrap框架下载与使用
我们主要的部分是结构(表单)和行为(验证表单),而样式不是重点。可以使用一些开源的css框架或者使用自己喜欢的样式,这些在网上都可以找到,我使用的Bootstrap框架
Bootstrap框架下载
Bootstrap有压缩版的,也有没压缩版的。如果有兴趣可以下载没压缩版的阅读源码 Bootstrap下载
Bootstrap框架使用
前端代码一般是放在webapp目录下,可以在webapp目录下创建一个views文件夹,存放前端代码
左击webapp按Ctrl+N输入folder后next
输入文件夹名views后finish
前端分为前台和后台,还可以在views文件下继续细分为前台(before文件夹),后台(after文件夹),这样可以保持项目结构简洁清晰
由于bootstrap框架,前台,后台都会用的到,所以可以在views文件夹创建一个css子文件夹存放bootstrap,然后在html文件中的head标签内添加引用即可,我的引用如下
<link
href="${pageContext.request.contextPath}/views/css/bootstrap.min.css"
rel="stylesheet">
在EL表达式通过page域获取请求域,然后通过请求域获取项目名即contextPath,这样就可以引用到这个文件
前端页面结构
注册页面主要的结构就是表单,里面包含一些组件(文本框,密码框,选择框,提交按钮)
左击before文件夹按Ctrl输入jsp,创建register.jsp,添加如下代码
<body class="gray-bg">
<div class="middle-box text-center loginscreen animated fadeInDown">
<div>
<h3>注册Resource library+</h3>
<form class="m-t" role="form"
action="${pageContext.request.contextPath}/register"
onsubmit="return checkRegisterForm(this);" method="post">
<div class="form-group">
<p>
<input type="text" class="form-control" name="userId"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="账号" required><span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="password" class="form-control"
name="userPassword" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)" placeholder="密码"
required><span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="password" class="form-control"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
name="userPasswordConfirm" placeholder="确认密码"
required> <span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="text" class="form-control" name="userName"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="姓名" required><span></span>
</p>
</div>
<div class="form-group">
<select class="form-control" id="user_level" name="userLevel">
<option value="学生">学生</option>
<option value="教师">教师</option>
</select>
</div>
<br>
<div class="form-group" id="student_class_form">
<p>
<input type="text" class="form-control" id="student_class"
name="userClass" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="班级" required>
<span></span>
</p>
</div>
<div class="form-group">
<p>
<input class="form-control" type="text" name="veryCode"
value="" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)" placeholder="验证码" />
<img height="25" src="${pageContext.request.contextPath}/getcode"
alt="看不清,换一张" onclick="change(this)"><span></span>
</p>
</div>
<button type="submit" class="btn btn-primary block full-width m-b">
注册
</button>
<p class="text-muted text-center">
<small>已经有一个账户?</small>
</p>
<a class="btn btn-sm btn-white btn-block" href="login.jsp">登录</a>
</form>
</div>
</div>
</body>
html代码结构就不详细说明了,不懂得可以查文档,这里主要讲一下重要的内容
我们给表单,组件,图片绑定的事件处理onfocus(获取焦点事触发),onblur(失去焦点时触发),onsubmit(提交表单时触发),onclick(点击时触发)
后面在js会给出处理函数,就不详细说明了
在验证码的文本框input标签后添加一个img标签来显示验证码图片,src属性指定的是生成验证的servlet,下面来说验证码的实现
验证码的实现
验证码是通过在img标签中的src属性指定为servlet的url,这样servlet就会把验证码通过输出流响应给客户端,下面给出生成验证码的servlet
创建servlet的方式在前面我已经讲过了,这里就不再重复了
在controller层的user包下创建CodeServlet.java,添加如下代码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, Object> codeMap = CodeUtil.generateCodeAndPic();
HttpSession session = req.getSession();
session.setAttribute("code", codeMap.get("code").toString());
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", -1);
resp.setContentType("image/jpeg");
ServletOutputStream sos;
try {
sos = resp.getOutputStream();
ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
sos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
通过上面代码我们知道,真正生成验证码的是工具类CodeUtil。而servlet主要功能调用工具类的方法生成验证码并放在session中,起到封装的作用。然后通过输出流响应给客户端
在utils包下创建工具类CodeUtil,代码如下
public class CodeUtil {
private static int width = 100;
private static int height = 20;
private static int codeCount = 4;
private static int xx = 15;
private static int fontHeight = 18;
private static int codeY = 16;
private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
public static Map<String,Object> generateCodeAndPic() {
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics gd = buffImg.getGraphics();
Random random = new Random();
gd.setColor(Color.WHITE);
gd.fillRect(0, 0, width, height);
Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
gd.setFont(font);
gd.setColor(Color.BLACK);
gd.drawRect(0, 0, width - 1, height - 1);
gd.setColor(Color.BLACK);
for (int i = 0; i < 30; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
StringBuffer randomCode = new StringBuffer();
int red = 0, green = 0, blue = 0;
for (int i = 0; i < codeCount; i++) {
String code = String.valueOf(codeSequence[random.nextInt(36)]);
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
gd.setColor(new Color(red, green, blue));
gd.drawString(code, (i + 1) * xx, codeY);
randomCode.append(code);
}
Map<String,Object> map =new HashMap<String,Object>();
map.put("code", randomCode);
map.put("codePic", buffImg);
return map;
}
}
完成到这里我们就可以测试验证码的功能了,启动项目,在浏览器地址栏输入http://localhost:8080/web_resource/views/before/register.jsp
刷新页面,也就是在请求一次servlet,这样验证码就跟着改变,后面会在js实现点击图片更改验证码
效果如下
前端页面表单验证
浏览器对所有get请求都有缓存功能,而请求js是get请求,所以可以把全部的js代码单独放在一个js文件中,这样浏览器只需请求一次js,效率就提高了
在views文件夹下创建js子文件夹存放js代码
下面给出的所有js代码都放在一个js文件中,我的是function.js。在页面结构的body标签之前引用即可,我的引用如下
<script src="${pageContext.request.contextPath}/views/js/function.js"></script>
</body>
使用js点击图片改变验证码
可以通过js实现点击图片改变验证码,这很简单。在img标签中想更改图片,其实就改变src的属性值。
但是我们请求的是一个servlet,所以url不能改,能改的只有查询串(即请求参数)。
请求参数要各不相同,使用Date类获取本地当前系统时间就可以实现,js代码如下
function change(img) {
img.src = getWebRootPath() + "/getcode?" + new Date().getTime();
}
这里使用的获取web根路径的函数,js代码如下
function getWebRootPath() {
var a = window.document.location.href;
var b = window.document.location.pathname;
var pos = a.indexOf(b);
var path = a.substring(0, pos);
a = a.substring(a.indexOf("/") + 2, a.length);
a = a.substring(a.indexOf("/") + 1, a.length);
var pathName = a.substring(0, a.indexOf("/"));
return path + "/" + pathName;
}
在注册页面结构中已经在img标签中使用了onclick点击事件函数,调用的正是change函数。所有可以直接测试了
效果如下
使用jQuery动态改变班级字段
jQuery是js库,它可以简化我们对DOM的操作
有两个版本的 jQuery 可供下载:
- Production version - 用于实际的网站中,已被精简和压缩。
- Development version - 用于测试和开发(未压缩,是可读的代码)
这两个版本都可以从 jQuery.com 下载。
在views文件夹的js子文件夹中添加jQuery,然后在head标签中进行引用即可,我的引用如下
<script
src="${pageContext.request.contextPath}/views/js/jquery-2.1.1.js"></script>
当在选择框选择学生时有班级文本框,但选择教师时是没有班级文本框的,下面通过jQuery实现这种效果,代码如下
$("#user_level").change(function() {
if ($('#user_level').val() == "学生") {
$('#student_class_form').show();
} else {
$('#student_class_form').hide();
$('#student_class').attr("required", false);
}
});
测试效果
使用jQuery和Ajax异步验证表单
jQuery前面已经介绍过了,这里主要讲一下Ajax技术
简短地说,AJAX 是与服务器交换数据的,在不重载整个网页的情况下,AJAX 通过后台加载数据,并在网页上进行显示。
用户注册时用的账号可能已经是注册过,由于账号是唯一的(即主键),这时如果不在客户端先进行检测的就提交表单到服务器就会报错
同理,用户在表单输入的验证码也需要在客户端先进行检测
要想在不把表单数据发送到服务器的情况下验证用户名是否存在,验证码是否输入正确,就需要使用Ajax
它可以请求服务器(servlet)获取数据,然后通过后台判断数据并进行相应处理,而不是加载整个网页(刷新,或者跳转页面)
下面验证账号是否存在的js核心代码,如下
var url = getWebRootPath() + "/check_user_id?id=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
$.get(url, function(data) {
if (data == "false") {
msgBox.html('账号不能使用!');
msgBox.addClass('error');
flagId = false;
} else {
flagId = true;
}
});
这里只是单独给出来说明验证账号的原理,实际运行需要添加到事件函数中才能运行,后面会给出完整代码
同样的验证用户输入的验证码是否正确也是通过Ajax请求servlet,接受服务端响应的信息来判断对不对,这里代码就不再给出
在注册页面结构中,已经给表单组件绑定了onblur(失去焦点时执行)事件,且事件处理函数都是一样的checkBlurRegisterItem(this)
这样就可以在这里集中验证表单是否为空,密码和确认密码是否一样,账号是否已存在,验证码是否正确了,完整代码如下
var flagId = false;
var flagPassword = false;
var flagPasswordConfirm = false;
var flagName = false;
var flagClass = true;
var flagCode = false;
function checkBlurRegisterItem(obj) {
var msgBox = $(obj).next('span');
switch ($(obj).attr('name')) {
case "userId":
if (obj.value == "") {
msgBox.html('账号不能为空');
msgBox.addClass('error');
flagId = false;
} else {
var url = getWebRootPath() + "/check_user_id?id=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
$.get(url, function(data) {
if (data == "false") {
msgBox.html('账号不能使用!');
msgBox.addClass('error');
flagId = false;
} else {
flagId = true;
}
});
}
break;
case "userPassword":
if (obj.value == "") {
msgBox.html('密码不能为空');
msgBox.addClass('error');
flagPassword = false;
} else {
flagPassword = true;
}
break;
case "userPasswordConfirm":
if (obj.value == "") {
msgBox.html('确认密码不能为空');
msgBox.addClass('error');
flagPasswordConfirm = false;
} else if ($(obj).val() != $('input[name="userPassword"]').val()) {
msgBox.html('两次输入的密码不一致');
msgBox.addClass('error');
flagPasswordConfirm = false;
} else {
flagPasswordConfirm = true;
}
break;
case "userName":
if (obj.value == "") {
msgBox.html('姓名不能为空');
msgBox.addClass('error');
flagName = false;
} else {
flagName = true;
}
break;
case "userClass":
if (obj.value == "") {
msgBox.html('班级不能为空');
msgBox.addClass('error');
}
break;
case "veryCode":
var numshow = $(obj).next().next();
if (obj.value == "") {
numshow.html('验证码不能为空');
numshow.addClass('error');
flagCode = false;
} else {
var url = getWebRootPath() + "/check_user_code?num=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
$.get(url, function(data) {
if (data == "false") {
numshow.html('验证码输入有误');
numshow.addClass('error');
flagCode = false;
} else {
flagCode = true;
}
});
}
break;
}
}
验证账号是否存在以及验证是否正确都是请求servlet来判断,下面分别给出
检测验证码是否正确的servlet
在controller层的user包下创建CheckUserCode.java,代码如下
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String num = request.getParameter("num");
HttpSession session = request.getSession();
String sysCode = (String) session.getAttribute("code");
PrintWriter out = response.getWriter();
if (sysCode.equals(num)) {
out.print("true");
} else {
out.print("false");
}
out.close();
}
完成到这步,就可以进行小测试,检查验证码效果,由于编写了servlet需要重启项目
测试效果
提示不太醒目,我们可以在注册页面中添加内联样式来修改一下。只需在head标签添加以下代码即可
<style>
.form-group p .error {
display: inline-block;
border: 1px solid #ff855a;
background-color: #ffe8e0;
height: 25px;
line-height: 25px;
padding: 0px 20px;
margin-left: 20px;
}
</style>
检测账号是否存在的servlet
在controller层的user包中创建CheckUserId.java,代码如下
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
String id = request.getParameter("id");
UserServiceImpl userServiceImpl = new UserServiceImpl();
int count = 0;
try {
count = userServiceImpl.checkIdRepeated(id);
} catch (SQLException e) {
e.printStackTrace();
}
PrintWriter out = response.getWriter();
if(count > 0 ){
out.print("false");
}else{
out.print("true");
}
out.close();
}
代码的功能注释已经详细说明,这里我们在说明一下之前搭建的三层架构:
显然这里检查账号是否存在的servlet是处于控制层,而就是说检查账号不是它实现的,它只是起到控制作用,比如它调用了service层的业务逻辑方法checkIdRepeated
下面我们只需要在service层的UserService接口中添加检查账号是否存在的业务功能,然后在UserServiceImpl实现类中重写即可
在接口中添加的业务逻辑方法如下
public int checkIdRepeated(String userId) throws SQLException;
在实现类重写接口方法
@Override
public int checkIdRepeated(String userId) throws SQLException {
int i = userDao.selectById(userId);
return i;
}
dao层的实现和service层是一样的,这里我就直接给出代码
在dao层的UserDao接口中添加检索账号的抽象方法
public int selectById(String userId) throws SQLException;
在dao层的UserDaoImpl实现类中重写接口方法
@Override
public int selectById(String userId) throws SQLException {
String sql = "select * from user where user_id=?";
resultSet = JDBCUtil.executeQuery(sql, userId);
int i = 0;
if (resultSet.next()) {
i = 1;
}
return i;
}
至此,检查账号是否存在的代码已经全部编写完成,测试之前需要重启项目,同时需要在数据库中添加一个账号
测试效果
在前面我们给表单组件绑定了失去焦点时执行的函数checkRegisterForm(this),我们知道如果表单组件输入为空等等,则会显示一个提示框
但是如果用户再次点击表单组件时,提示信息不会消失,所以我们还得给所有表单绑定一个获取焦点时执行的事件函数focusItem(this),然后再js编写处理逻辑即可,代码如下
function focusItem(obj) {
if ($(obj).attr('name') == 'veryCode') {
$(obj).next().next().html('').removeClass('error');
} else {
$(obj).next('span').html('').removeClass('error');
}
}
这里就不再演示效果了,等到验证注册功能时会看到这个效果的
我们编写了表单组件的失去焦点和获取焦点的js事件函数,但是当点击提交时表单还需要验证一次,这里只需给form表单组件绑定一个事件onsubmit,处理函数为checkRegisterForm(this),代码如下
function checkRegisterForm(form) {
var elements = form.getElementsByTagName('input');
for (var i = 0; i < elements.length; i++) {
if (elements[i] != null) {
if (elements[i].getAttribute("onblur")) {
checkBlurRegisterItem(elements[i]);
}
}
}
if (flagId && flagPassword && flagPasswordConfirm &&
flagName && flagClass && flagCode) {
return true;
} else {
return false;
}
}
通过获取所有input标签并返回一个包含input对象的数组,然后遍历判断input标签是否包含onblur属性,若有则调用失去焦点时执行的函数checkBlurRegisterItem
注册功能综合测试
ok,大功告成,到现在注册功能所有代码均编写完成,现在就可以来测试注册功能了
测试效果
教师注册效果就不再演示了,下一篇会更新在此三层架构完善登录功能
|