我想做类似sina博客的评论系统,注册用户可以无刷新同时登陆并发表评论,成功后刷新评论列表,思路是利用xmlhttprequest做异步调用,前段js代码如下:
function createXmlHttp() {
var xmlHttp = null; if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) { throw new Error("您的浏览器版本过低,请更新浏览器"); }
}
return xmlHttp;
}//获得评论数据
function getList()
{
var mid = GetID();//获得当前书籍的ID
var url = "http://localhost/tcshu/bookCommentServer.aspx?action=getList&bookID="+ mid;
var gbxmlhttp = createXmlHttp();
gbxmlhttp.onreadystatechange=function()
{
if(gbxmlhttp.readyState==4&&gbxmlhttp.status==200)
{ $get("commentList").innerHTML = gbxmlhttp.responseText;//获得数据并填充评论列表
}
}
gbxmlhttp.open('GET',url,true);
gbxmlhttp.send(null);
}//发布评论
function PostComment(userName,body)
{
if(userName==null){ window.alert("昵称不能为空!");return;}
var id = GetID();
//var params="action=postComment&userName="+ encodeURIComponent(userName)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id);
var url = "bookCommentServer.aspx?action=postComment&userName="+ encodeURIComponent(userName)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id);
var gbxmlhttp2 = createXmlHttp();
gbxmlhttp2.open('GET',url,true);
//gbxmlhttp2.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//gbxmlhttp2.send(params);
$get("commentArea").value= "";
getList();
}//判断用户是否登录
function IsVali(){
var vali;
var url = "CheckLoginServer.aspx?action=isVali";
var gbxmlhttp3 = createXmlHttp();
gbxmlhttp3.onreadystatechange=function()
{
if(gbxmlhttp3.readyState==4&&gbxmlhttp3.status==200)
{ vali = gbxmlhttp3.responseXML.getElementsByTagName("isVali")[0].firstChild.data;
}
}
gbxmlhttp3.open('GET',url,true);
gbxmlhttp3.send(null);
return vali;
}//获得注册用户名
function GetUserName(){
var uname;
var url = "CheckLoginServer.aspx?action=getName";
var gbxmlhttp4 = createXmlHttp();
gbxmlhttp4.onreadystatechange=function()
{
if(gbxmlhttp4.readyState==4&&gbxmlhttp4.status==200)
{ uname = gbxmlhttp4.responseXML.getElementsByTagName("userName")[0].firstChild.data;
}
}
gbxmlhttp4.open('GET',url,true);
gbxmlhttp4.send(null);
return uname;
}//用户登陆
function CheckUser(){
var userName=$get("login_name").value;
var password=$get("login_pass").value;
var url = "CheckLoginServer.aspx?action=validateUser&userName="+ encodeURIComponent(userName)+ "&password=" + encodeURIComponent(password);
var xmlCheckUser=createXmlHttp();
xmlCheckUser.onreadystatechange=function()
{
if(xmlCheckUser.readyState==4&&xmlCheckUser.status==200)
{
var mes=xmlCheckUser.responseXML.getElementsByTagName("message")[0].firstChild.data;
var val=xmlCheckUser.responseXML.getElementsByTagName("passed")[0].firstChild.data;
if(val=="True")
{ //若登陆则获得注册用户名
$get("ctl00_ContentPlaceHolder1_BookComment1_registerName").innerText=GetUserName();
$get("ctl00_ContentPlaceHolder1_BookComment1_userInfo").style.display="none";
return true;
}
else
{
$get("errorText").innerHTML=mes;
return false;
}
}
}
xmlCheckUser.open("GET",url,true);
xmlCheckUser.send(null);
}//发布评论按钮调用此函数
function sendBookComment(){
var userName;
var hasVali=IsVali();
var ck=$get("anonymity");
if(hasVali=="True")
{
if(ck!=null&&ck.checked==true)
{
window.alert("您已登录!");
getList();
$get("ctl00_ContentPlaceHolder1_BookComment1_registerName").innerText=GetUserName();
$get("ctl00_ContentPlaceHolder1_BookComment1_userInfo").style.display="none";
}else{
userName=GetUserName();
}
}
else if(hasVali=="False")
{
var ck=$get("anonymity");
if(ck!=null&&ck.checked==true)
{
userName=$get("comment_anonyous").value;
}else{
var hasLogin = CheckUser();
if(hasLogin==true)
{
userName=GetUserName();
}
}
}
var ubody=$get("commentArea");
if(ubody.value=="")
{
alert('留言不能为空');
return;
}
PostComment(userName,ubody.value);
}
运行后可以无刷新登陆,也可以发布,但是总会出现“灵异现象”:总会出现登陆却没有发布,或者发布了评论列表却没有刷新,或者干脆连验证是否登录与获得登陆用户名都会返回undefined,但若调试时设置断点却有返回值;FF浏览器下却根本无法执行,这样一些莫名其妙的问题。
经分析应该是应用了太多异步调用机制,导致有些值还没有传递,接着就执行下步操作了。
那么如何进行多次异步操作时保证数据一致呢?
(第一步、无刷新登陆)
(第二步、发布评论)
(第三步、刷新评论列表)
function createXmlHttp() {
var xmlHttp = null; if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) { throw new Error("您的浏览器版本过低,请更新浏览器"); }
}
return xmlHttp;
}//获得评论数据
function getList()
{
var mid = GetID();//获得当前书籍的ID
var url = "http://localhost/tcshu/bookCommentServer.aspx?action=getList&bookID="+ mid;
var gbxmlhttp = createXmlHttp();
gbxmlhttp.onreadystatechange=function()
{
if(gbxmlhttp.readyState==4&&gbxmlhttp.status==200)
{ $get("commentList").innerHTML = gbxmlhttp.responseText;//获得数据并填充评论列表
}
}
gbxmlhttp.open('GET',url,true);
gbxmlhttp.send(null);
}//发布评论
function PostComment(userName,body)
{
if(userName==null){ window.alert("昵称不能为空!");return;}
var id = GetID();
//var params="action=postComment&userName="+ encodeURIComponent(userName)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id);
var url = "bookCommentServer.aspx?action=postComment&userName="+ encodeURIComponent(userName)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id);
var gbxmlhttp2 = createXmlHttp();
gbxmlhttp2.open('GET',url,true);
//gbxmlhttp2.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//gbxmlhttp2.send(params);
$get("commentArea").value= "";
getList();
}//判断用户是否登录
function IsVali(){
var vali;
var url = "CheckLoginServer.aspx?action=isVali";
var gbxmlhttp3 = createXmlHttp();
gbxmlhttp3.onreadystatechange=function()
{
if(gbxmlhttp3.readyState==4&&gbxmlhttp3.status==200)
{ vali = gbxmlhttp3.responseXML.getElementsByTagName("isVali")[0].firstChild.data;
}
}
gbxmlhttp3.open('GET',url,true);
gbxmlhttp3.send(null);
return vali;
}//获得注册用户名
function GetUserName(){
var uname;
var url = "CheckLoginServer.aspx?action=getName";
var gbxmlhttp4 = createXmlHttp();
gbxmlhttp4.onreadystatechange=function()
{
if(gbxmlhttp4.readyState==4&&gbxmlhttp4.status==200)
{ uname = gbxmlhttp4.responseXML.getElementsByTagName("userName")[0].firstChild.data;
}
}
gbxmlhttp4.open('GET',url,true);
gbxmlhttp4.send(null);
return uname;
}//用户登陆
function CheckUser(){
var userName=$get("login_name").value;
var password=$get("login_pass").value;
var url = "CheckLoginServer.aspx?action=validateUser&userName="+ encodeURIComponent(userName)+ "&password=" + encodeURIComponent(password);
var xmlCheckUser=createXmlHttp();
xmlCheckUser.onreadystatechange=function()
{
if(xmlCheckUser.readyState==4&&xmlCheckUser.status==200)
{
var mes=xmlCheckUser.responseXML.getElementsByTagName("message")[0].firstChild.data;
var val=xmlCheckUser.responseXML.getElementsByTagName("passed")[0].firstChild.data;
if(val=="True")
{ //若登陆则获得注册用户名
$get("ctl00_ContentPlaceHolder1_BookComment1_registerName").innerText=GetUserName();
$get("ctl00_ContentPlaceHolder1_BookComment1_userInfo").style.display="none";
return true;
}
else
{
$get("errorText").innerHTML=mes;
return false;
}
}
}
xmlCheckUser.open("GET",url,true);
xmlCheckUser.send(null);
}//发布评论按钮调用此函数
function sendBookComment(){
var userName;
var hasVali=IsVali();
var ck=$get("anonymity");
if(hasVali=="True")
{
if(ck!=null&&ck.checked==true)
{
window.alert("您已登录!");
getList();
$get("ctl00_ContentPlaceHolder1_BookComment1_registerName").innerText=GetUserName();
$get("ctl00_ContentPlaceHolder1_BookComment1_userInfo").style.display="none";
}else{
userName=GetUserName();
}
}
else if(hasVali=="False")
{
var ck=$get("anonymity");
if(ck!=null&&ck.checked==true)
{
userName=$get("comment_anonyous").value;
}else{
var hasLogin = CheckUser();
if(hasLogin==true)
{
userName=GetUserName();
}
}
}
var ubody=$get("commentArea");
if(ubody.value=="")
{
alert('留言不能为空');
return;
}
PostComment(userName,ubody.value);
}
运行后可以无刷新登陆,也可以发布,但是总会出现“灵异现象”:总会出现登陆却没有发布,或者发布了评论列表却没有刷新,或者干脆连验证是否登录与获得登陆用户名都会返回undefined,但若调试时设置断点却有返回值;FF浏览器下却根本无法执行,这样一些莫名其妙的问题。
经分析应该是应用了太多异步调用机制,导致有些值还没有传递,接着就执行下步操作了。
那么如何进行多次异步操作时保证数据一致呢?
(第一步、无刷新登陆)
(第二步、发布评论)
(第三步、刷新评论列表)
你的提议很好,将多步操作并作一步提交由服务器做判断。我将尝试将登陆与发布一并操作。
但是我考虑的情况比这个稍显复杂:
1.注册用户可以无刷新登陆并发布评论
2.登陆后用户直接发布评论(不再有用户名与密码)
3.匿名用户通过匿名发布评论(没有密码)
ps特殊情况
1.若用户在其它页面登陆此页面仍未显示登陆状态时发布信息,则会弹出错误,确定后动态刷新为登录状态。
2.若登陆session过期或其它页面注销登录此页面仍显示登录状态发布信息,则会弹出用户名错误并什么都不做。
(后期会加入session验证码)
在后台处理时,判断这个标志位就行了
if(未登陆){
//todo:登陆操作,取用户名密码校验
//成功则发表评论
//失败则提示用户错误
}else{
if(登陆时){
//发表评论
}else{//匿名
//发表评论
}
}PS:这个功能还可以直接放在过滤器中
最后返回状态,刷新列表
function sendBookComment2(){
var userName;
var pass;
var lag;
var ck=$get("anonymity");
if(ck!=null){
if(ck.checked==true){
userName=$get("comment_anonyous").value;
lag=0;
}else if(ck.checked==false){
userName=$get("login_name").value;
pass=$get("login_pass").value;
lag=1;
}
}else if(ck==null){
lag=2;
}
var ubody=$get("commentArea");
if(ubody.value=="")
{
alert('留言不能为空');
return;
}
postComment2(userName,pass,ubody.value,lag);
}function postComment2(userName,pass,body,lag){
//if(userName==null){ window.alert("昵称不能为空!");return;}
var id = GetID();
//var params="action=postComment&userName="+ encodeURIComponent(userName)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id);
var url = "http://localhost/tcshu/bookCommentServer.aspx?action=postComment&userName="+ encodeURIComponent(userName)+"&pass="+encodeURIComponent(pass)+ "&body=" + encodeURIComponent(body)+ "&bookID=" + encodeURIComponent(id)+"&lag="+encodeURIComponent(lag);
var gbxmlhttp2 = createXmlHttp();
gbxmlhttp2.onreadystatechange=function()
{
if(gbxmlhttp2.readyState==4&&gbxmlhttp2.status==200)
{
//接收4个参数passed-是否通过登录mes-登陆错误信息success-是否发布成功errortext-发布错误信息
var passed=gbxmlhttp2.responseXML.getElementsByTagName("passed")[0].firstChild.data;
var mes=gbxmlhttp2.responseXML.getElementsByTagName("message")[0].firstChild.data;
var success=gbxmlhttp2.responseXML.getElementsByTagName("success")[0].firstChild.data;
var errortext=gbxmlhttp2.responseXML.getElementsByTagName("errortext")[0].firstChild.data;
if(passed!=null&&passed=="True")
{
$get("<%=registerName.ClientID %>").innerText=$get("login_name").value;
$("ul").remove("<%=userInfo.ClientID %>");//用jQuery移除掉了,display掉还会被找到。
//$get("<%=userInfo.ClientID %>").style.display="none";
}else if(passed!=null&&passed=="False")
{
$get("errorText").innerHTML=mes;
}
if(success!=null&&success=="True")
{
getList();
}else if(success!=null&&success=="False")
{
alert(errortext);
}
}
}
gbxmlhttp2.open('GET',url,true);
gbxmlhttp2.send(null);
//gbxmlhttp2.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//gbxmlhttp2.send(params);
$get("commentArea").value= "";
}
由于要从responseXML里接受数据,但有时会没有不分返回数据如:用户匿名发布就不会有登陆成功的信息,但是必须接收此信息,不然会产生“未知对象”的错误。所以迫不得已,server的代码就变成下面这个丑样了:
if (Request.QueryString["userName"] == "undefined" && Request.QueryString["pass"] == "undefined")
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (!string.IsNullOrEmpty(Request.QueryString["lag"]) && Request.QueryString["lag"] == "2")
{
string userName = HttpContext.Current.User.Identity.Name;
string body = Request.QueryString["body"];
PostComment(userName, body);
Response.ContentType = "text/xml";
Response.Write("<response>" + "<passed>" + "hasVali" + "</passed>" + "<message>" + "hasVali" + "</message>" + "<success>" + "True" + "</success>" + "<errortext>" + "登陆用户发布!" + "</errortext>" + "</response>");
}
}
else
{
Response.ContentType = "text/xml";
Response.Write("<response>" + "<passed>" + "noVali" + "</passed>" + "<message>" + "noVali" + "</message>" + "<success>" + "False" + "</success>" + "<errortext>" + "昵称不能为空!" + "</errortext>" + "</response>"); }
}
else if (Request.QueryString["userName"] != "undefined" && Request.QueryString["pass"] == "undefined")
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Response.ContentType = "text/xml";
Response.Write("<response>" + "<success>" + "False" + "</success>" + "<errortext>" + "用户已登录!" + "</errortext>" + "<passed>" + "hasVali" + "</passed>" + "<message>" + "hasVali" + "</message>" + "</response>");
}
else
{
if (!string.IsNullOrEmpty(Request.QueryString["lag"]) && Request.QueryString["lag"] == "0")
{
string userName = Request.QueryString["userName"];
string body = Request.QueryString["body"];
PostComment(userName, body);
Response.ContentType = "text/xml";
Response.Write("<response>" + "<passed>" + "anony" + "</passed>" + "<message>" + "anony" + "</message>" + "<success>" + "True" + "</success>" + "<errortext>" + "匿名用户!" + "</errortext>" + "</response>");
}
}
}
else if (Request.QueryString["userName"] != "undefined" && Request.QueryString["pass"] != "undefined")
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Response.ContentType = "text/xml";
Response.Write("<response>" + "<passed>" + "hasVali" + "</passed>" + "<message>" + "hasVali" + "</message>" + "<success>" + "False" + "</success>" + "<errortext>" + "用户已登录!" + "</errortext>" + "</response>");
}
else
{
if (!string.IsNullOrEmpty(Request.QueryString["lag"]) && Request.QueryString["lag"] == "1")
{
Response.ContentType = "text/xml";
string userName = Request.QueryString["userName"];
string pass = Request.QueryString["pass"];
bool isValidate = ValidateUser(userName, pass);
string message = "用户名或密码错误!登录失败!";
if (isValidate)
{
message = "登陆成功!";
string body = Request.QueryString["body"];
PostComment(userName, body);
}
string textxml; textxml = "<response>" + "<success>" + isValidate.ToString() + "</success>" + "<errortext>" + "登陆发布!" + "</errortext>" + "<passed>" + isValidate.ToString() + "</passed>" + "<message>" + message + "</message>" + "</response>";
Response.Write(textxml);
}
}
}
为了避免客户端没有返回值的错误,所有的response的数据都必须包含这四个数据,即使没用也要硬编出来。
而if判断还变成了与“undefined”比较这种难看的格式。