小型购物网站(二):注册用例

如果数据不完整,则不能将数据写入到数据库中。这就需要验证数据了,也就是表单验证。

后台表单验证

逻辑图解析:我们在浏览器点击注册,跳转到注册页面(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);
    });	 	
});

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值