分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

使用jquery.form.js的ajaxsubmit方法提交数据的Bug

发布时间:2023-09-06 01:51责任编辑:赖小花关键词:js
???

周五同事遇到一个很奇怪的问题,调到下班,虽然问题解决了,但是不知道问题的具体原因,回来翻了翻代码,才发现症结所在,下面就分享出来,供遇到同样问题的同行们参考:

先把问题描述一下,做的功能是使用ajax向后台来提交数据,为了向用户进行很好的错误提示,后台中将出现错误时的错误原因返回给前端,前端使用jquery.form.js的ajaxsubmit来提交数据,并在success方法中提示“操作成功”,在error方法中提示错误原因。整个form提交的数据包括一些简单的input和一个文件的上传。下面是代码:

前端JSP代码:

Java代码
  1. <formmethod="post"enctype="multipart/form-data">
  2. <inputtype="file"name="posterUrlUploadPath"title="上传图片"/>
< form id ="wfAuditForm" method ="post" enctype ="multipart/form-data">< input type ="file" name ="posterUrlUploadPath" id ="posterUrlUploadPath" class ="fileUpload" title ="上传图片" />

前端JS代码:

Java代码
  1. $("#wfAuditForm").ajaxSubmit({
  2. type:‘post‘,
  3. url:"data/resource/picture/save",
  4. success:function(data){
  5. alert("success");
  6. $("#wfAuditForm").resetForm();
  7. },
  8. error:function(XmlHttpRequest,textStatus,errorThrown){
  9. alert("error");
  10. }
  11. });
$("#wfAuditForm").ajaxSubmit({ ???????????????????type: ‘post‘, ???????????????????url: "data/resource/picture/save" , ???????????????????success: function(data){ ???????????????????????alert( "success"); ???????????????????????$( "#wfAuditForm").resetForm(); ???????????????????}, ???????????????????error: function(XmlHttpRequest, textStatus, errorThrown){ ???????????????????????alert( "error"); ???????????????????} ???????????????});

后台:

Java代码
  1. publicvoidsave(HttpServletResponseresponse,HttpServletRequestrequest,IntegerhasUpload,PictureResourcepic){
  2. response.setStatus(HttpServletResponse.SC_CONFLICT);
  3. }
public void save(HttpServletResponse response, HttpServletRequest request, Integer hasUpload,PictureResource pic) { ????response.setStatus(HttpServletResponse. SC_CONFLICT);}

问题是当提交的数据中file标签里面有值的话(有文件需要上传),即时后台返回的状态码不是200,也会触发js的success方法。

当然第一时间想到的是不是返回的状态码不是预期中的,于是使用了firebug对于通信进行了抓包,抓包后发现返回的的确是409(SC_CONFLICT),但是触发的还是success上面。后来意识到这种问题只有当有文件需要上传的时候才会发现,因此怀疑form提交的时候返回了两次response,一次是文件流从客户端到服务端的过程,一次是真正的数据提交的过程,因此使用了wireshark抓了几次包,抓出来的报文显示的确是只返回了一次response(当有文件上传的时候,会出现一个redirect的报文,这个在后面的博文中会有分析),这个说明跟http的网络通信及服务端处理没有关系。

问题到底出在什么地方呢?再次回过头来读jquery.form.js的代码,发现这段代码中有这么一段很可疑:

Js代码
  1. varfound=false;
  2. for(varj=0;j<files.length;j++)
  3. if(files[j])
  4. found=true;
  5. if(options.iframe||found)//options.iframeallowsusertoforceiframemode
  6. fileUpload();
  7. else
  8. $.ajax(options);
var found = false; ???for ( var j=0; j < files.length; j++) ???????if (files[j]) ???????????found = true; ???if (options.iframe || found) // options.iframe allows user to force iframe mode ???????fileUpload(); ???else ???????$.ajax(options);

这段代码的第一个for循环是遍历form中所有的file标签,一旦其中的一个file标签里面有值,就将found设置了true。后面的代码就是根据found来进行判断了,如果found为真(有需要上传的文件)将调用fileUpload方法,否则调用jquery的ajax方法。根据上面的现象描述,问题可能出现在fileUpload方法中。下面我们再看fileUpload方法:

Js代码
  1. //privatefunctionforhandlingfileuploads(hattiptoYAHOO!)
  2. functionfileUpload(){
  3. varform=$form[0];
  4. varopts=$.extend({},$.ajaxSettings,options);
  5. varid=‘jqFormIO‘+$.fn.ajaxSubmit.counter++;
  6. var$io=$(‘<iframename="‘+id+‘"/>‘);
  7. vario=$io[0];
  8. varop8=$.browser.opera&&window.opera.version()<9;
  9. if($.browser.msie||op8)io.src=‘javascript:false;document.write("");‘;
  10. $io.css({position:‘absolute‘,top:‘-1000px‘,left:‘-1000px‘});
  11. varxhr={//mockobject
  12. responseText:null,
  13. responseXML:null,
  14. status:0,
  15. statusText:‘n/a‘,
  16. getAllResponseHeaders:function(){},
  17. getResponseHeader:function(){},
  18. setRequestHeader:function(){}
  19. };
  20. varg=opts.global;
  21. //triggerajaxglobaleventssothatactivity/blockindicatorsworklikenormal
  22. if(g&&!$.active++)$.event.trigger("ajaxStart");
  23. if(g)$.event.trigger("ajaxSend",[xhr,opts]);
  24. varcbInvoked=0;
  25. vartimedOut=0;
  26. //takeabreathsothatpendingrepaintsgetsomecputimebeforetheuploadstarts
  27. setTimeout(function(){
  28. $io.appendTo(‘body‘);
  29. //jQuery‘seventbindingdoesn‘tworkforiframeeventsinIE
  30. io.attachEvent?io.attachEvent(‘onload‘,cb):io.addEventListener(‘load‘,cb,false);
  31. //makesureformattrsareset
  32. varencAttr=form.encoding?‘encoding‘:‘enctype‘;
  33. vart=$form.attr(‘target‘);
  34. $form.attr({
  35. target:id,
  36. method:‘POST‘,
  37. encAttr:‘multipart/form-data‘,
  38. action:opts.url
  39. });
  40. //supporttimout
  41. if(opts.timeout)
  42. setTimeout(function(){timedOut=true;cb();},opts.timeout);
  43. form.submit();
  44. $form.attr(‘target‘,t);//resettarget
  45. },10);
  46. functioncb(){
  47. if(cbInvoked++)return;
  48. io.detachEvent?io.detachEvent(‘onload‘,cb):io.removeEventListener(‘load‘,cb,false);
  49. varok=true;
  50. try{
  51. if(timedOut)throw‘timeout‘;
  52. //extracttheserverresponsefromtheiframe
  53. vardata,doc;
  54. doc=io.contentWindow?io.contentWindow.document:io.contentDocument?io.contentDocument:io.document;
  55. xhr.responseText=doc.body?doc.body.innerHTML:null;
  56. xhr.responseXML=doc.XMLDocument?doc.XMLDocument:doc;
  57. if(opts.dataType==‘json‘||opts.dataType==‘script‘){
  58. varta=doc.getElementsByTagName(‘textarea‘)[0];
  59. data=ta?ta.value:xhr.responseText;
  60. if(opts.dataType==‘json‘)
  61. eval("data="+data);
  62. else
  63. $.globalEval(data);
  64. }
  65. elseif(opts.dataType==‘xml‘){
  66. data=xhr.responseXML;
  67. if(!data&&xhr.responseText!=null)
  68. data=toXml(xhr.responseText);
  69. }
  70. else{
  71. data=xhr.responseText;
  72. }
  73. }
  74. catch(e){
  75. ok=false;
  76. $.handleError(opts,xhr,‘error‘,e);
  77. }
  78. //orderingofthesecallbacks/triggersisodd,butthat‘show$.ajaxdoesit
  79. if(ok){
  80. opts.success(data,‘success‘);
  81. if(g)$.event.trigger("ajaxSuccess",[xhr,opts]);
  82. }
  83. if(g)$.event.trigger("ajaxComplete",[xhr,opts]);
  84. if(g&&!--$.active)$.event.trigger("ajaxStop");
  85. if(opts.complete)opts.complete(xhr,ok?‘success‘:‘error‘);
  86. //cleanup
  87. setTimeout(function(){
  88. $io.remove();
  89. xhr.responseXML=null;
  90. },100);
  91. };
// private function for handling file uploads (hat tip to YAHOO!) ???function fileUpload() { ???????var form = $form[0]; ???????var opts = $.extend({}, $.ajaxSettings, options); ???????????????var id = ‘jqFormIO‘ + $.fn.ajaxSubmit.counter++; ???????var $io = $(‘<iframe  name="‘ + id + ‘" />‘); ???????var io = $io[0]; ???????var op8 = $.browser.opera && window.opera.version() < 9; ???????if ($.browser.msie || op8) io.src = ‘javascript:false;document.write("");‘; ???????$io.css({ position: ‘absolute‘, top: ‘-1000px‘, left: ‘-1000px‘ }); ???????var xhr = { // mock object ???????????responseText: null, ???????????responseXML: null, ???????????status: 0, ???????????statusText: ‘n/a‘, ???????????getAllResponseHeaders: function() {}, ???????????getResponseHeader: function() {}, ???????????setRequestHeader: function() {} ???????}; ???????????????var g = opts.global; ???????// trigger ajax global events so that activity/block indicators work like normal ???????if (g && ! $.active++) $.event.trigger("ajaxStart"); ???????if (g) $.event.trigger("ajaxSend", [xhr, opts]); ???????????????var cbInvoked = 0; ???????var timedOut = 0; ???????????????// take a breath so that pending repaints get some cpu time before the upload starts ???????setTimeout(function() { ???????????$io.appendTo(‘body‘); ???????????// jQuery‘s event binding doesn‘t work for iframe events in IE ???????????io.attachEvent ? io.attachEvent(‘onload‘, cb) : io.addEventListener(‘load‘, cb, false); ???????????????????????// make sure form attrs are set ???????????var encAttr = form.encoding ? ‘encoding‘ : ‘enctype‘; ???????????var t = $form.attr(‘target‘); ???????????$form.attr({ ???????????????target: ??id, ???????????????method: ?‘POST‘, ???????????????encAttr: ‘multipart/form-data‘, ???????????????action: ??opts.url ???????????}); ???????????// support timout ???????????if (opts.timeout) ???????????????setTimeout(function() { timedOut = true; cb(); }, opts.timeout); ???????????form.submit(); ???????????$form.attr(‘target‘, t); // reset target ???????}, 10); ???????????????function cb() { ???????????if (cbInvoked++) return; ???????????????????????io.detachEvent ? io.detachEvent(‘onload‘, cb) : io.removeEventListener(‘load‘, cb, false); ???????????var ok = true; ???????????try { ???????????????if (timedOut) throw ‘timeout‘; ???????????????// extract the server response from the iframe ???????????????var data, doc; ???????????????doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; ???????????????xhr.responseText = doc.body ? doc.body.innerHTML : null; ???????????????xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; ???????????????????????????????if (opts.dataType == ‘json‘ || opts.dataType == ‘script‘) { ???????????????????var ta = doc.getElementsByTagName(‘textarea‘)[0]; ???????????????????data = ta ? ta.value : xhr.responseText; ???????????????????if (opts.dataType == ‘json‘) ???????????????????????eval("data = " + data); ???????????????????else ???????????????????????$.globalEval(data); ???????????????} ???????????????else if (opts.dataType == ‘xml‘) { ???????????????????data = xhr.responseXML; ???????????????????if (!data && xhr.responseText != null) ???????????????????????data = toXml(xhr.responseText); ???????????????} ???????????????else { ???????????????????data = xhr.responseText; ???????????????} ???????????} ???????????catch(e){ ???????????????ok = false; ???????????????$.handleError(opts, xhr, ‘error‘, e); ???????????} ???????????// ordering of these callbacks/triggers is odd, but that‘s how $.ajax does it ???????????if (ok) { ???????????????opts.success(data, ‘success‘); ???????????????if (g) $.event.trigger("ajaxSuccess", [xhr, opts]); ???????????} ???????????if (g) $.event.trigger("ajaxComplete", [xhr, opts]); ???????????if (g && ! --$.active) $.event.trigger("ajaxStop"); ???????????if (opts.complete) opts.complete(xhr, ok ? ‘success‘ : ‘error‘); ???????????// clean up ???????????setTimeout(function() { ????????????????$io.remove(); ????????????????xhr.responseXML = null; ???????????}, 100); ???????};

很明显,这是通过使用隐藏iframe来模拟ajax实现的文件上传(参见该方法的介绍博文《谈谈使用iFrame模拟Ajax的问题》)。注意在方法cb中有这么一段代码:

Java代码
  1. catch(e){
  2. ok=false;
  3. $.handleError(opts,xhr,‘error‘,e);
  4. }
  5. //orderingofthesecallbacks/triggersisodd,butthat‘show$.ajaxdoesit
  6. if(ok){
  7. opts.success(data,‘success‘);
  8. if(g)$.event.trigger("ajaxSuccess",[xhr,opts]);
  9. }
catch(e){ ???????????????ok = false; ???????????????$.handleError(opts, xhr, ‘error‘, e); ???????????} ???????????// ordering of these callbacks/triggers is odd, but that‘s how $.ajax does it ???????????if (ok) { ???????????????opts.success(data, ‘success‘); ???????????????if (g) $.event.trigger("ajaxSuccess", [xhr, opts]); ???????????}

从这个代码中可以看出,仅仅是当出现异常的时候(关于js异常的情况,请参见介绍博文《Javascript的异常处理介绍》),才会触发我们设定的error方法,其余情况都会触发success,也就是说即时http返回的不是200,而是其他的错误码,只要不出现异常就不会触发error方法!

找到问题原因了,我们怎么来实现根据http返回的状态码来进行相应的处理呢?一种策略是将状态码写到返回的是text的文本中,然后在客户端根据文本进行判断。或许另外一种方法是重写这个cb方法,在其中根据http的状态码来进行不同的处理,不过我还没有找到获取返回的状态码的方法。

互联网码农一枚,欢迎微博互粉,进行交流:http://weibo.com/icemanhit

?

使用jquery.form.js的ajaxsubmit方法提交数据的Bug

原文地址:https://www.cnblogs.com/jpfss/p/8963731.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved