如果数据不完整,则不能将数据写入到数据库中。这就需要验证数据了,也就是表单验证。
后台表单验证
逻辑图解析:我们在浏览器点击注册,跳转到注册页面(regist.jsp),注册表单页面添加注册信息然后提交表单(form中的action属性决定了表单提交的位置,一般需要加web应用的路径-->/Easymall/RegistServlet,但是我们不能加EasyMall,因为当前easymall是默认web应用,它映射的路径是一个空字符串,加了之后会报404)到servlet,需要servlet来执行注册逻辑,用来提示您注册成功/失败。如果成功,3秒后自动跳转首页(定时刷新),所以浏览器在3秒后会自动请求首页(index.jsp)。如果没有注册成功即表单验证出现问题,就把错误提示信息存入request作用域,将请求转发给regist.jsp,regist.jsp从request作用域中取出错误提示信息,显示在页面上。看上去你收到的还是原来的注册页面,只不过它多了错误提示信息(其实是一个新的页面)。
创建servlet之前我们首先讨论一下web中的路径问题:
在regist.jsp中,引入css使用了相对路径href="css/regist.css",相对的是当前资源的父路径
1.如果通过超链接直接访问该页面,它的父路径就是项目的根路径:/localhost/EasyMall/css/regist.css(因为咱们是默认web应用,所以/EasyMall可以省略)
2.但是,如果是通过Servlet转发请求到regist.jsp,那么当前regist.jsp的父路径是Servlet的父路径
RegistServlet-> /RegistServlet->父路径就是web应用的根路径(我的理解是服务器启动之后RegistServlet会被放在WebRoot/WEB-INF/classes文件夹下,根路径为WebRoot,和regist.css的根路径一样),不会存在css找不到的情况:/localhost/EasyMall/css/regist.css(因为咱们是默认web应用,所以/EasyMall可以省略)
我写的这个测试类TestServlet-> /servlet/TestServlet ->web应用/servlet:/localhost/EasyMall/servlet/css/regist.css,多了一级servlet目录,导致找不到regist.css文件。(这个类我们不需要写,只是为了我们更好的理解路径问题)
解决方案:
1.创建RegistServlet时不加/servlet
2.如果要加/servlet,就将regist.jsp中的路径改为绝对路径:/项目url/资源的路径/EasyMall/css/regist.css。咱们是默认web应用,没有/EasyMall,绝对路径直接写成:/css/regist.jsp
servlet执行的注册逻辑:点击注册,服务器返回regist.jsp,填写完注册表单,提交表单时提交到了RegistServlet(代表当前Servlet响应的是用户注册的业务的操作):
1.处理乱码问题:请求乱码/应答乱码。
// 请求乱码--POST请求
request.setCharacterEncoding("utf-8");
// 应答乱码
response.setContentType("text/html;charset=utf-8");
2.接收表单参数(req.getParameter)
//例如用户名和密码如下,其它的表单参数以此类推,有几项就要写几行这样的代码
String username = request.getParameter("username");
String password = request.getParameter("password");
3.表单验证
可以将重复用到的代码提取到一个工具类中,将这些类放在同一个工具包中,我们为了方便区分前后台的表单验证,可以让后台表单验证的错误提示信息显示在表单的最上方,让前台表单验证的错误提示信息显示在每个表单项后面,以下代码段是后台表单验证在regist.jsp中的代码。
<!-- 在form标签的table标签下添加以下代码 -->
<tr>
<!-- 我们想显示一行错误提示信息,所以用一个td就够了 -->
<!-- colspan表示你的错误提示信息所占的行数 -->
<!-- style中的属性表示错误提示信息字体颜色为红色并且居中显示 -->
<td colspan="2" style="text-align:center;color:red">
<!-- 我们可以在<%= %>中添加java代码 -->
<!-- 这行代码表示从request作用域中拿到错误提示信息 -->
<!-- 我们如果从没有值的request作用域中取值,会显示null -->
<!-- 如果不想看到null,就写一个三目运算符,如果为null,则返回一个空串,不为null则返回对应的错误提示信息 -->
<%=request.getAttribute("errMsg")==null?"":request.getAttribute("errMsg") %>
</td>
</tr>
我们想要提交表单到我们写的RegistServlet就需要修改regist.jsp中form标签的action属性了,action决定了你的表单提交给谁。代码如下:
<!-- action中应该写servlet映射的路径 -->
<!-- 正常情况下,你应该将映射路径写全(如果你部署了tomcat,就应该写web应用映射的路径):/EasyMall/RegistServlet -->
<!-- 但是咱们这儿不可以写,因为咱们的项目是默认的web应用,它映射的路径是一个空字符串,所以不可以写,写了会报404 -->
<form action="/RegistServlet" method="POST" onSubmit="return formObj.checkForm()">
1)非空验证
不为null(String的trim方法-->去除首尾空格),且trim之后不为空串。如果用户名为空,那么会将请求转发给regist.jsp,请求转发之后,相当于方法的调用,后面的代码还会执行,会继续判断密码是否为空,如果也为空,它又会尝试执行一次转发,这时
就会执行多次转发,产生问题。所以我们在执行转发的代码后面添加一个return语句,相当于doPost方法执行完毕,后面的代码不执行,就不会出问题了。
public class WebUtils {
/**
* 用来验证字符串是否为null或空串的方法
* @param str 被验证的字符串
* @return true-字符串为null或者trim之后为空串
* false-不为null且trim之后不为空串
*/
public static boolean isEmpty(String str){
if(str==null || "".equals(str.trim())){
return true;
}
return false;
}
}
// 以下是验证用户名不为空的代码,其它参数以此类推
if (WebUtils.isEmpty(username)) {
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "用户名不能为空");
// 将请求转发给regist.jsp
// 请求转发之后,后面的代码还会执行,会导致多次转发
// 所以我们加一个return语句,即:如果转发了,就return
// 相当于我doPost方法执行完毕,后面的代码不执行
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
但是我们发现出现错误提示信息之后我们之前输入的数据就没有了,为此我们可以做表单回填,即:用代码将用户输入的信息回填到错误提示页面中,给用户的感觉就是页面没有变(自己之前填入的合法的信息没有消失),只出现了错误提示信息。怎么做呢?我们在regist.jsp中用户名对应的input标签下添加一个value属性,它的值为从request对象中的parameter集合中获取用户提交的信息,代码如下:
<!-- 这里只写了用户名,其它表单项以此类推 -->
<tr>
<td class="tds">用户名:</td>
<td>
<input type="text" name="username" value="<%=request.getParameter("username")==null?"":request.getParameter("username")%>"/>
<span></span>
</td>
</tr>
补充一下,request对象中有两个集合:parameter是用来保存用户提交的表单数据的map集合;attribute是用来保存错误提示信息的map集合,我们可以直接从parameter集合中得到用户的表单数据,而不必去RegistServlet中再写setAttribute("username",username);来给attribute集合中添加用户表单数据了。
2)两次密码一致验证
将密码和确认密码输入的内容进行equals比较,执行逻辑和非空验证一样
if (!password.equals(password2)) {
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "两次密码应该一致");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
3)邮箱格式验证
除了编写对应的正则表达式以外,验证逻辑和非空验证一样
//如:abc@123.163.com
//^表示开始,$表示结束(表示完整的匹配)
String reg="^\\w+@\\w+(\\.+\\w)+$";
if (!email.matches(reg)) {
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "邮箱格式不正确");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
4)用户名是否存在验证
(RegistServlet做不了,因为用户名是否存在是数据库说的算)。我们在Mysql数据库中新建一个database叫easymall,在其中创建一个用户表(user),一般我们将数据库的执行语句放在工具包中的一个文件easymall.sql中,方便以后别人查看。我们想检查当前注册的用户名之前是否有人用过,这时候java程序就要和数据库交互了(所以这条验证就需要发请求给MySql,看当前用户名是否存在,这时候就用到JDBC了),现实开发中一般使用数据库连接池,因为它要比传统的方式更有效率。我们可以创建一个SourceFolder文件夹,把C3P0.xml文件放进去,这样的话这个文件在服务器运行的时候就会自动进入到WEB-INF文件夹下的classes目录。
我们先写一个JDBC的工具类,在其中写获取连接对象和关闭连接两个方法,记住导的是java.sql下的包,不然测试的时候会报类转换异常(ClassCastException)
public class JDBCUtils {
//获取c3p0数据库连接池对象
private static ComboPooledDataSource ds = new ComboPooledDataSource();
/**
* 通过数据库连接池获取一个连接对象
* @return 一个连接对象 或 null
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//关闭连接
public static void close(Connection conn,Statement ps,ResultSet rs){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
然后我们就可以使用用户传入的用户名到数据库中查数据了(这是RegistServlet中的代码)
// 使用用户传入的用户名到数据库查数据,如果有数据,说明用户名已经存在
//sql查询语句
String sql1 = "select * from user where username=?";
//获取连接对象
Connection conn = null;
//获取预编译的sql语句对象
PreparedStatement ps = null;
ResultSet rs = null;
conn = JDBCUtils.getConnection();
try {
//得到连接对象
conn = JDBCUtils.getConnection();
//绑定sql语句
ps = conn.prepareStatement(sql1);
//绑定参数
ps.setString(1, username);
//执行查询操作
rs = ps.executeQuery();
if (rs.next()) {// 说明用户名存在
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "用户名已存在");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
} catch (Exception e) {
e.printStackTrace();
// 防止出现异常后前台看不到
// 向request作用域中添加错误提示信息
// request.setAttribute("errMsg", "注册出现异常,请稍候重试");
// 将请求转发给regist.jsp
// request.getRequestDispatcher("/regist.jsp").forward(request,response);
// return;
// 开发时使用的便捷方案,产品上线时是要删除的
throw new RuntimeException("验证用户名时数据库出现异常" + e.getMessage());
} finally {
JDBCUtils.close(conn, ps, rs);
}
5)验证码验证
验证码:用来确认操作者是否是人类的技术。(防止某些计算机程序的恶意操作)
用户请求regist.jsp,regist.jsp返回一个html的内容给浏览器,浏览器在解析html的内容时发现了img标签,img标签的src属性指向的是"/ValiImageServlet",浏览器会自动的发送请求给ValiImageServlet,ValiImageServlet收到请求后会创建一个VarifyCode对象vc,然后vc.drawImage(...)把一个流放进去,这就生成了一个验证码图片了,同时调用vc.getCode()得到正确的验证码,然后ValiImageServlet将验证码的图片返回给用户。
之后用户输入注册信息点击“提交”按钮时,就将表单提交到了RegistServlet,在RegistServlet中做表单验证中的“验证码验证”,它的逻辑就是将用户表单提交的验证码和正确的验证码比较。
所以我们现在需要在RegistServlet中拿到ValiImageServlet产生的一个数据。之前我们的Request作用域不行,因为它是作用在一个请求链上的,而我们这次不止一次请求;之后我们想用ServletContext(Application)作用域,看起来是可以的,如果你的网站只有一个人访问!因为如果超过一个人访问,那么后来的数据会覆盖之前的数据,结果就是用户输验证码输到地老天荒都不正确!
以上两种方案不行是因为它们没有解决验证码验证的核心问题:每一个用户的验证码数据应该是彼此独立互不干扰的!!!
而 Session作用域完美解决了这个问题,用户1访问服务器,服务器提供Session1来存储用户1的验证码图片,用户2访问服务器,服务器提供Session2来存储用户2的验证码图片,每一个用户都拥有自己专属的Session来存储自己的数据,是不是很赞?然后它们把各自Session的id返回给用户,用户再访问RegistServlet时,携带了各自Session的id到RegistServlet,RegistServlet通过Session的id从对应的Session中拿到之前通过vc.getCode()保存的验证码。并且Session作用域随着web应用的存在而一直存在着。
综上所述,Session作用域是验证码验证问题的最佳解决方案!!!
我们写一个用来生成验证码的工具类VerifyCode.java,这个不需要掌握,理解大概意思就行
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
/**
* 动态生成图片
*/
public class VerifyCode {
// {"宋体", "华文楷体", "黑体", "华文新魏", "华文隶书", "微软雅黑", "楷体_GB2312"}
private static String[] fontNames = { "宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312" };
// 可选字符
//"23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";
private static String codes = "23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";
// 背景色
private Color bgColor = new Color(255, 255, 255);
// 基数(一个文字所占的空间大小)
private int base = 30;
// 图像宽度
private int width = base * 4;
// 图像高度
private int height = base;
// 文字个数
private int len = 4;
// 设置字体大小
private int fontSize = 22;
// 验证码上的文本
private String text;
private BufferedImage img = null;
private Graphics2D g2 = null;
/**
* 生成验证码图片
*/
public void drawImage(OutputStream outputStream) {
// 1.创建图片缓冲区对象, 并设置宽高和图像类型
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 2.得到绘制环境
g2 = (Graphics2D) img.getGraphics();
// 3.开始画图
// 设置背景色
g2.setColor(bgColor);
g2.fillRect(0, 0, width, height);
StringBuffer sb = new StringBuffer();// 用来装载验证码上的文本
for (int i = 0; i < len; i++) {
// 设置画笔颜色 -- 随机
// g2.setColor(new Color(255, 0, 0));
g2.setColor(new Color(getRandom(0, 150), getRandom(0, 150),getRandom(0, 150)));
// 设置字体
g2.setFont(new Font(fontNames[getRandom(0, fontNames.length)], Font.BOLD, fontSize));
// 旋转文字(-45~+45)
int theta = getRandom(-45, 45);
g2.rotate(theta * Math.PI / 180, 7 + i * base, height - 8);
// 写字
String code = codes.charAt(getRandom(0, codes.length())) + "";
g2.drawString(code, 7 + i * base, height - 8);
sb.append(code);
g2.rotate(-theta * Math.PI / 180, 7 + i * base, height - 8);
}
this.text = sb.toString();
// 画干扰线
for (int i = 0; i < len + 2; i++) {
// 设置画笔颜色 -- 随机
// g2.setColor(new Color(255, 0, 0));
g2.setColor(new Color(getRandom(0, 150), getRandom(0, 150),
getRandom(0, 150)));
g2.drawLine(getRandom(0, 120), getRandom(0, 30), getRandom(0, 120),
getRandom(0, 30));
}
//绘制边框
//设置边框的颜色
g2.setColor(Color.GRAY);
//绘制边框
g2.drawRect(0, 0, width-1, height-1);
// 4.保存图片到指定的输出流
try {
ImageIO.write(this.img, "JPEG", outputStream);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
// 5.释放资源
g2.dispose();
}
}
/**
* 获取验证码字符串
* @return
*/
public String getCode() {
return this.text;
}
/*
* 生成随机数的方法
*/
private static int getRandom(int start, int end) {
Random random = new Random();
return random.nextInt(end - start) + start;
}
public static void main(String[] args) throws Exception {
VerifyCode vc = new VerifyCode();
vc.drawImage(new FileOutputStream("d:/vc.jpg"));
System.out.println("执行成功~!");
}
}
然后写一个servlet叫ValiImageServlet(用来处理验证码逻辑的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.tedu.util.VerifyCode;
public class ValiImageServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.接收请求
//2.调用工具类,生成验证码图片
VerifyCode vc = new VerifyCode();
//3.将生成的验证码图片存入response实体中
vc.drawImage(response.getOutputStream());
//4控制浏览器不要缓存验证码图片
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
//将生成的验证码文本内容输出到控制台
String text = vc.getCode();
System.out.println("text="+text);
//获取用户的Session对象
HttpSession session = request.getSession();
//将正确的验证码文本传入Session作用域
session.setAttribute("text", text);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
然后在RegistServlet中添加相应的代码,我们将验证码的验证放到最前面
// 5)验证码验证
if (WebUtils.isEmpty(valistr)) {
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "验证码不能为空");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,
response);
return;
}else{
//验证码不为空,执行验证码内容验证
//获取保存在Session中的正确的验证码
//false表示不给这个用户创建Session
HttpSession session = request.getSession(false);
boolean flag=true;//默认验证没有问题
if(session==null || session.getAttribute("text")==null){
//没有Session对象,或者Session中没有正确的验证码文本
flag=false;
}else{
String text=(String) session.getAttribute("text");
if(!valistr.equals(text)){
//用户输入的文本和正确文本不一致
flag=false;
}
}
if(flag==false){
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "验证码错误");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,
response);
return;
}
}
4.将数据存入数据库(JDBC)
如果数据库突然宕机,就还得执行和表单验证一样的错误处理流程(这是RegistServlet中的代码)
//sql语句
String sql2= "insert into user values(null,?,?,?,?)";
//获取连接对象
Connection conn2 = null;
//获取预编译的sql语句对象
PreparedStatement ps2=null;
try {
//得到连接对象
conn2=JDBCUtils.getConnection();
//绑定sql语句
ps2=conn2.prepareStatement(sql2);
//绑定参数
ps2.setString(1, username);
ps2.setString(2, password);
ps2.setString(3, nickname);
ps2.setString(4, email);
//执行更新操作,i表示影响的行数,如果插入成功,行数大于0
int i = ps2.executeUpdate();
if(i>0){//插入成功
// 5.保存成功-提示成功信息,定时刷新到首页
response.getWriter().write("<h1 style='text-align:center;color:green'>恭喜您,注册成功!!!3秒后自动跳转首页</h1>");
// 实现定时刷新,从规范的角度讲,应该这么写,如果部署项目,这个方法就有值了
response.setHeader("refresh", "3;url=" + request.getContextPath()+ "index.jsp");
}else{
// 如果当前web应用时虚拟主机默认的web应用,它的映射路径是"",因此调用
// request.getContextPath()方法,返回的也是一个空字符串
// System.out.println("path="+request.getContextPath()+"---");
// 5.保存失败-添加错误提示信息,转发regist.jsp
// 向request作用域中添加错误提示信息
request.setAttribute("errMsg", "注册出现异常,请稍候重试");
// 将请求转发给regist.jsp
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("注册数据出现异常:"+e.getMessage());
}finally{
JDBCUtils.close(conn2, ps2, null);
}
5.给用户返回提示信息(代码实现已经存在于上面的代码段中)
这一步与数据库的交互有关,如果数据成功存入数据库,则提示成功信息,并且实现定时刷新;如果存入数据失败,则向request作用域中添加错误提示信息并转发regist.jsp页面
前台表单验证
既然有了前台的表单验证,后台表单验证还是否还有必要?
后台表单验证依旧有必要!!!因为我们自己写的表单页面中有js。但是别人可以仿照我们的表单页面自己写一个表单页面
,仿照我们写的表单页面中的action,也可以将他写的页面提交给服务器!!!其中没有js代码!!!这样他就跳过了前台
的表单验证!!!如果没有后台表单验证,他将为所欲为!!!
注意:前台表单验证是帮助服务器拦截无效的请求,大部分不是坏人的用户通过前台表单验证之后再提交给服务器,大大减
轻了服务器的压力。
点击注册用户按钮时,如何触发js写的表单验证?
我们在form标签中添加一个属性叫onSubmit(表示表单提交时)。它的值为一个方法,这个方法在表单提交时会执行,这个方法就是表单验证的方法。由于我们希望验证通过之后,表单再提交,所以我们在onsubmit的属性值开头加一个return。如果加了return之后报错,我们可以右键regist.jsp-->MyEclipse-->点击Exclude From Validation(这是告诉MyEclipse将这个文件排除在验证之外),如果还想让MyEclipse做检查,右键regist.jsp-->MyEclipse-->点击Run Validation(手动让它验证一次)。
<form action="/RegistServlet" method="POST" onSubmit="return formObj.checkForm()">
onSubmit中的值:以这个方法的返回值作为这个地方的值。如果返回的是true,表单就会提交,如果返回的是false,表单就不提交。
我们在regist.jsp中的<head>标签下添加<script>标签,在其中键入java代码。并且把表单验证的方法,封装到一个自定义的对象当中(仿json格式,其中有key:value,value可以是值,也可以是方法)。可以在checkForm方法中添加必要的表单验证逻辑,这里我们将非空验证的逻辑抽取到checkNull方法中,我们需要引入jquery的文件(因为我们需要用jquery来获取input框中的值),将其放到项目的WebRoot目录下,然后在regist.jsp中的head标签下引入这个文件。
<script type="text/javascript" src="/js/jquery-1.4.2.js" ></script>
将获取错误提示信息的逻辑抽取到setMsg方法中。我们需要在每个表单项的input标签下添加与它并列的兄弟标签span,将错误提示信息放到span中。注意:验证码的span标签要添加到img标签后边。
<tr>
<td class="tds">验证码:</td>
<td>
<input type="text" name="valistr"/>
<img id="valiImage" src="/ValiImageServlet" width="" height="" alt="" />
<span></span>
</td>
</tr>
1.非空验证
<script type="text/javascript">
var formObj={
"checkForm":function(){
//1.非空验证
var flag=true;//控制表单提交的变量,默认为true
//验证用户名不为空
flag=this.checkNull("username","用户名不能为空")&&flag;
//验证密码不为空
flag=this.checkNull("password","密码不能为空")&&flag;
},
//用来判断input值是否为null的方法
"checkNull":function(name,msg){
//1)拿到对应的input框的值(jQuery)
var value=$("input[name='"+name+"']").val();
//2)判断是否为空
if($.trim(value)==""){
//如果值为空,则调用设置消息的方法
//将错误提示信息显示在该input的后面
this.setMsg(name, msg);
//表单不应该提交
return false;
}else{
//将之前添加的错误提示清空
this.setMsg(name,"");
//表单可以提交
return true;
}
},
//设置提示消息的方法
"setMsg":function(name,msg){
//获取name指定的input所有后面的span
//然后将传入的错误提示信息显示在该span的内部,并设置css样式
//由于验证码的span标签在img标签后边,用next("span")不行
//所以我们用nextAll("span")
$("input[name='"+name+"']").nextAll("span").html(msg).css("color","red");
//伪代码
//$input=$("input[name='"+name+"']");
//$span=$input.next();
//$span.html(msg);
//$span.css("color","red");
}
};
2.两次密码一致验证
将验证密码的逻辑抽取到checkPassword()中,checkPassword()方法与checkForm()方法并列
//2.两次密码一致验证,写在checkForm()方法中
flag=this.checkPassword("password", "两次密码应该一致")&&flag;
"checkPassword":function(name,msg){
//获取password和password2的input的值
var psd1=$("input[name='"+name+"']").val();
var psd2=$("input[name='"+name+"2']").val();
//当两个值都不为空串时再进行密码一致验证
if($.trim(psd1)!=""&&$.trim(psd2)!=""){
if(psd1!=psd2){
//添加错误提示信息
this.setMsg(name+"2", msg);
return false;
}else{
//清空之前的错误提示信息
this.setMsg(name+"2","");
return true;
}
}
return false;
},
3.邮箱格式验证
将验证逻辑抽取到checkEmail()方法中,checkEmail()方法与checkForm()方法并列,逻辑与密码一致验证类似
//3.邮箱格式验证
flag=this.checkEmail("email","邮箱格式不正确")&&flag;
"checkEmail":function(name,msg){
//获取email的input的值
var email=$("input[name='"+name+"']").val();
//当邮箱的值不为空串是再进行格式判断
if($.trim(email)!=""){
//i代表忽略大小写,g代表全局检查
//var reg = new RegExp("","ig");
//123@163.com.cn
var reg = /^\w+@+\w+(\.\w+)+$/;
if(!reg.test(email)){
//设置错误提示信息
this.setMsg(name,msg);
return false;
}else{
//清空之前添加的错误提示信息
this.setMsg(name,"");
return true;
}
}
return false;
},
4.用户名不存在验证
我们在regist.jsp页面中的用户名输入框中输入数据(alex)后,将鼠标点击到其他地方时就会自动发送一个ajax请求到服务器,服务器中需要有对应的Servlet组件来响应,之前的RegistServlet不能用来响应,因为它是用来响应表单提交的请求的。所以我们单独提供一个Servlet叫AJAXCheckUsernameServlet,用来接收请求,获取请求参数;去数据库查有没有用户名是alex;将查询的结果返回给浏览器。浏览器获取到服务器返回的信息,显示在页面上。
1)在web.xml中配置字符集
<!-- 配置全局的字符集 -->
<context-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</context-param>
2)我们新建一个servlet,叫AJAXCheckUsernameServlet。在AJAXCheckUsernameServlet中处理乱码问题和与数据库的交互
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.tedu.util.JDBCUtils;
public class AJAXCheckUsernameServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取web.xml中配置的字符集
String encode = this.getServletContext().getInitParameter("encode");
//1.处理乱码问题
//应答乱码
response.setContentType("text/html;charset="+encode);
//2.获取请求参数
String username= request.getParameter("username");
//请求乱码GET请求
byte[] array=username.getBytes("iso8859-1");
username=new String(array,encode);
//3.查询数据库,看用户名是否存在
String sql="select * from user where username=?";
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
ps=conn.prepareStatement(sql);
ps.setString(1, username);
rs=ps.executeQuery();
if(rs.next()){//用户名已存在
response.getWriter().write("该用户名已存在");
}else{
response.getWriter().write("恭喜您,该用户名可以使用");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.close(conn, ps, rs);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3)设置用户名<span>标签的id
<tr>
<td class="tds">用户名:</td>
<td>
<input type="text" name="username" value="
<%=request.getParameter("username")==null?"":request.getParameter("username")%>"/>
<span id="msg_username"></span>
</td>
</tr>
4)给用户名不存在验证提供一个失去焦点事件
//文档就绪事件,当当前文档加载完成后,会自动调用
//blur相当于失去焦点,它后面的function封装了失去焦点之后要执行的逻辑
$("input[name='username']").blur(function(){
//验证用户名是否为空
var flag=formObj.checkNull("username", "用户名不能为空");
//发送ajax
if(flag){//说明username输入框不为空
//发送ajax,this代表当前方法的调用者(当前input的值)
var url="AJAXCheckUsernameServlet?username="+$(this).val();
//不需要data,因为已经拼接在了上面的url后边
//也不需要callback来将消息添加到span组件中
//因为load方法是通过一个组件来调用的
//当ajax请求收到应答后,会将应答的内容自动填充到该组件(span)内部
$("#msg_username").load(url);
}
});
5.验证码验证
失去焦点后提示错误信息的实现
之后我们为了用户体验更好,我们可以做失去焦点之后的错误提示信息。(就是用户在输入信息之后,用鼠标点击旁边的空
白之后,就会立刻出现提示信息),我们写一个文档就绪的方法,在其中添加文档就绪时需要用到的逻辑,包括点击验证码图片之后可以更换验证码图片。
//当input失去焦点时,马上执行对应的表单验证
//需要为每个input添加一个失去焦点的事件
//添加一个文档就绪事件,在事件中为每个input添加对应的方法
//$();表示文档就绪
$(function(){
//文档就绪事件,当当前文档加载完成后,会自动调用
//blur相当于失去焦点,它后面的function封装了失去焦点之后要执行的逻辑
//这里以用户名为例,其他表单项以此类推
$("input[name='username']").blur(function(){
formObj.checkNull("username", "用户名不能为空");
});
//为img标签添加一个点击事件,用于点击验证码图片后更换验证码图片
$("#valiImage").click(function(){
//每次点击修改src属性的值,在后面拼接一个不同的参数,这里我们用毫秒值
//获取当前事件的毫秒值表示
var timeStr=new Date().getTime();
//将毫秒值拼接在url后面,保证每次点击url的值都不同
var url="/ValiImageServlet?time="+timeStr;
//使用生成的url给img标签的src属性赋值
//修改属性值
$("#valiImage").attr("src",url);
});
});