Ajax注册表单用户名实时验证
Ajax注册表单用户名实时验证
1.1.1 摘要
很多时候在网站上注册时,我们会发现,注册表单通常需要检查用户名和电子邮件地址的可用性;从而确保用户之间不拥有相同的用户名和电子邮件地址;一些网站喜欢在用户提交填写的用户信息时,做信息可用性的检查,而一些网站会做实时的用户名和电子邮件地址可用性检查,例如:“用户名”文本框失去焦点时;就用户体验来说,实时的用户信息检查用户体验效果更好,而不是在表单提交后,告诉用户信息不符合系统要求。
下面截图是新浪微博的注册界面,它采用的是实时的用户信息检查,如:手机号码和用户名等信息。
图1新浪微博注册表单
本文目录
1.1.2 正文
假设,现在要求我们实现一个注册界面并且它采用实时方式检查用户信息是否符合系统的要求。
其中,注册界面包含:用户名,邮件地址,密码和微博地址等信息;实时检查:当文本框失去焦点时对信息进行实时检查,例如:“用户名”文本框失去焦点时,触发页面发送Ajax请求,到数据库中用户是否可用,接着把查询结果返回到前端页面中。
对于表单输入检查,我们将使用前一博文《自定义jQuery插件Step by Step》中,自定义表单信息检查插件对输入信息进行检查。
图2注册验证过程
注册表单设计
现在,让我们定义注册表单,它包含:用户名,邮件地址,密码和微博地址等信息,由于时间的关系,我们已经把注册表单界面的定义好了,具体实现代码如下:
<body> <div id="Div1"> <!-- Start Sign Up Form --> <form action="#signup-form" id="Form1"> <h2> Sign Up</h2> <fieldset> <div class="fieldgroup"> <label for="name"> Name</label> <input class="form_element" type="text" name="name" validation="required nameAvailable" /> <span class='availability_status'></span> </div> <div class="fieldgroup"> <label for="email"> Email</label> <input class="form_element" type="text" name="email" validation="email" /> <span></span> </div> <div class="fieldgroup"> <label for="password"> Password</label> <input class="form_element" type="password" name="password" validation="password" /> <span></span> </div> <div class="fieldgroup"> <label for="weibo"> Weibo</label> <input class="form_element" type="text" name="weibo" validation="url" /> <span></span> </div> <div class="fieldgroup"> <input type="submit" class="submit" value="Sign up"> <span></span> </div> </fieldset> <div class="fieldgroup"> <p> Already registered? <a href="http://www.cnblogs.com/rush">Sign in</a>.</p> </div> </form> <!-- End Sign Up Form --> </div> </body>
上面,我们实现了注册表单,它包含:用户名,邮件地址,密码和微博地址文本框,当“用户名”失去焦点时,发送Ajax请求实时检查用户名是否可用,并且动态地加载图片表示检查中和相应的检查结果。
图3注册表单
jQuery Validation插件
现在,我们需要修改一下前一博文中定义的表单检查控件,需要增加用户名检查和文本框失去焦点事件(blur event)。
首先,我们在Validation类型中,增加用户名检查方法,由于用户检查是通过发送Ajax请求,然后到数据库中查询是否存在该用户名来确定该用户名是否可用。
nameAvailable: { check: function(value) { if (value) { var dataString = 'username=' + value; var result; // Checking availability... // Loads checking image. $(".availability_status").html('<img src="loader.gif" align="absmiddle">'); // Sends ajax request to check the name is available or not. $.ajax({ type: "GET", url: "UserService.ashx", data: dataString, success: function(data) { // When the checking completed, then loaded corresponding css style. $('.availability_status').ajaxComplete(function(event, request, settings) { if (data == false) { $('.availability_status').html(''); $('.availability_status').removeClass('tick'); $('.availability_status').addClass('error'); return true; } else { $('.availability_status').html(''); $('.availability_status').removeClass('error'); $('.availability_status').addClass('tick'); return false; } }); } }); // Sends a asyn reqeust, return false temporary. return false; //// e.preventDefault(); } else { $('.availability_status').removeClass('tick'); $('.availability_status').removeClass('error'); return false; } }, msg: "", tip: "Should enter 4-30 characters, support letter, figures and _ or -" }
上面,我们完成了在Validation类型中增加nameAvailable()方法,它通过发送异步Ajax请求来检查用户名是否可用,在检查的过程中,在输入框右边动态地加载图片来表示检查中,当检查结束加载相应的图片表示用户是否可用。
接下来,我们将添加focus和blur事件,当文本框得到焦点时触发focus事件,提示输入信息的要求,当文本框失去焦点时触发blur事件,发生实时Ajax请求,例如:检查用户名是否可用。
由于,事件方法应该是Field类型公开的方法,确实我们也不想为每个Field对象都定义自己事件方法,所以我们通过原型链的方式公开事件方法focus()和blur(),具体实现如下:
// The prototype of Field type. Field.prototype = { // Public method. attach: function(event) { // The object refers to Field object. var obj = this; // When field lost focus, then invoked the validate method. if (event == "blur") { obj.field.bind("blur", function() { return obj.validate(); }); } // When field got focus, then invoked the hint method. if (event == "focus") { obj.field.bind("focus", function() { return obj.hint(); }); } } }
我们给Field的原型链添加了事件响应事件blur和focus,当失去焦点时触发Field的vlidate()方法,获得焦点时触发Field的hint()方法。
数据表设计
前面,我们提到注册表单的用户名可用性检查设计是通过发送Ajax请求,然后到数据库中查询用户名是否存在来确定用户名的可用性。
接下来,我们添加数据库表jk_user用来存储用户信息,它包括用户名、密码(注意:这里没有考虑隐私信息的加密存储)、显示名称和注册日期等,具体设计如下:
-- ============================================= -- Author: JKhuang -- Create date: 7/8/2012 -- Description: A table stores the user information. -- ============================================= CREATE TABLE [dbo].[jk_users]( -- This is the reference to Users table, it is primary key. [ID] [bigint] IDENTITY(1,1) NOT NULL, [user_login] [varchar](60) NOT NULL, [user_pass] [varchar](64) NOT NULL, [user_nicename] [varchar](50) NOT NULL, [user_email] [varchar](100) NOT NULL, [user_url] [varchar](100) NOT NULL, -- This field get the default from function GETDATE(). [user_registered] [datetime] NOT NULL CONSTRAINT [DF_jk_users_user_registered] DEFAULT (getdate()), [user_activation_key] [varchar](60) NOT NULL, [user_status] [int] NOT NULL CONSTRAINT [DF_jk_users_user_status] DEFAULT ((0)), [display_name] [varchar](250) NOT NULL )
图4数据表设计
服务端设计
前面,我们实现了用户表的设计,由于注册表单通过发送Ajax请求访问服务器端公开的方法,然后通过该公开方法访问数据库。
接下来,我们定义用户表(jk_user)的数据库访问对象(DAO),它包含方法IsAvailableUser()用来检查用户名是否可用,具体的实现如下:
/// <summary> /// The user dao manager. /// </summary> public class UserManager { private const string Query = "SELECT user_login, user_email, user_url FROM jk_users WHERE (user_login = @user_login)"; /// <summary> /// Initializes a new instance of the <see cref="UserManager"/> class. /// </summary> public UserManager() { } /// <summary> /// Determines whether the user is available or not, with specified username. /// </summary> /// <param name="userName">Name of the user.</param> /// <returns> /// <c>true</c> if is available user; otherwise, <c>false</c>. /// </returns> public bool IsAvailableUser(string userName) { using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLCONN2"].ToString())) using (var com = new SqlCommand(Query, con)) { // Pass user_login parameter to sql statement. com.Parameters.Add("@user_login", SqlDbType.VarChar).Value = userName; com.Connection.Open(); return !com.ExecuteReader().HasRows; } } }
现在,我们完成了数据库访问对象UserManager,它包含方法IsAvailableUser()来检查用户是否可用,如果该用户名已经存在返回false,反之返回true。
接下来,我们需要实现服务器端类UserService,让客户端通过它来调用UserManager中的IsAvailableUser()方法,首先我们创建一般处理程序(ASHX文件),实现ProcessRequest()方法,具体实现如下:
/// <summary> /// The user availability check service. /// </summary> public class UserService : IHttpHandler { public void ProcessRequest(HttpContext context) { // Response json type. context.Response.ContentType = "application/json"; context.Response.Charset = "utf-8"; var manager = new UserManager(); string userName = context.Request.QueryString["username"]; // Checks the username empty or not. if (string.IsNullOrEmpty(userName)) { throw new Exception("Username can't be empty"); } // Invokes the IsAvailableUser method. var result = manager.IsAvailableUser(userName); // Serializes data to json format. var json = new DataContractJsonSerializer(result.GetType()); json.WriteObject(context.Response.OutputStream, result); } // Whether can resuable by other handler or not. public bool IsReusable { get { return true; } } }
大家注意到UserService类实现了IHttpHandler接口,该接口包含一个方法ProcessRequest()方法和一个属性IsReusable;ProcessRequest()方法用于处理入站的Http请求,在默认情况下,UserService类把Response内容类型定义为application/json,这样我们就可以把数据通过JSON格式进行传输;IsReusable属性表示相同的处理程序是否可以用于多个请求,这里我们设置为true,那么处理程序可以用于多个请求。
前端实现
注册表单在检查用户名时会在输入框的右边动态地加载图片,这里我们使用CSS Sprite技巧实现图片的动态加载。
当页面加载时,不是加载每个单独图片,而是一次加载整个组合图片。这是一个了不起的改进,因为我们只需发送一个HTTP请求获取组合图片就OK了,它大大减少了HTTP请求的次数,减轻服务器压力,同时缩短了悬停加载图片所需要的时间延迟,使效果更流畅,不会停顿。
这里,我们使用可以一个在线的工具SpritePad设计组合图片和生成相应的CSS代码。
图5组合图片
上面,我们已经准备好组合图片了,接下来我们添加CSS代码动态加载“正确”和“出错”图片,具体实现如下:
/*CSS Sprite*/ /*Loads tick picture*/ .tick { width: 17px; height: 17px; margin: 6px 0 0; float: right; background: url(../images/tipicon.png); background-position: 0px -32px; display: block; /*text-decoration: none; vertical-align:middle;*/ } /*Loads error picture*/ span.error { width: 17px; height: 17px; margin: 6px 0 0; float: right; background: url(../images/tipicon.png); background-position: 0px -15px; display: block; /*text-decoration: none; vertical-align:middle;*/ }
接着,我们在nameAvailable()方法中的Ajax方法添加文件“UserService.ashx”请求,并传递用户名给服务器;在数据库中查询用户名是否已经存在,存在返回“false”,否则返回“true”。
// Sends ajax request to check the name is available or not. $.ajax({ type: "GET", url: "UserService.ashx", data: dataString, success: function(data) { // When the checking completed, then loaded corresponding css style. $('.availability_status').ajaxComplete(function(event, request, settings) { if (data == false) { $('.availability_status').html(''); $('.availability_status').removeClass('tick'); $('.availability_status').addClass('error'); return true; } else { $('.availability_status').html(''); $('.availability_status').removeClass('error'); $('.availability_status').addClass('tick'); return false; } }); } });
最后,我们在注册表单中添加调用自定义验证控件的jQuery代码,具体实现如下:
<script type="text/javascript"> // Sets display image size. pic = new Image(16, 16); pic.src = "loader.gif"; $(function() { // jQuery DOM ready function. // Get the form object. var signUpForm = $("#signup-form"); // Invokes the validation method. signUpForm.validation(); }); </script>
1.1.3 总结
本文主要介绍了Ajax注册表单的设计,通过发送Ajax请求方式实时地检查用户名的可用性,并且使用了自定义的表单验证插件检查输入信息的正确性。
注册验证过程的前端和后端设计,前端使用了表单验证插件,CSS Sprites技巧和Ajax;后端通过提供一般处理程序让客户端调用用户名可用性的检查方法IsAvailableUser()。
参考
- http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/css-sprite-sheets-best-practices-tools-and-helpful-applications/
- http://net.tutsplus.com/tutorials/javascript-ajax/submit-a-form-without-page-refresh-using-jquery/
- http://net.tutsplus.com/articles/news/how-to-make-ajax-requests-with-raw-javascript/