使用jquery.form.js的ajaxsubmit方法提交數據的Bug


周五同事遇到一個很奇怪的問題,調到下班,雖然問題解決了,但是不知道問題的具體原因,回來翻了翻代碼,才發現症結所在,下面就分享出來,供遇到同樣問題的同行們參考:

 

先把問題描述一下,做的功能是使用ajax向后台來提交數據,為了向用戶進行很好的錯誤提示,后台中將出現錯誤時的錯誤原因返回給前端,前端使用jquery.form.js的ajaxsubmit來提交數據,並在success方法中提示“操作成功”,在error方法中提示錯誤原因。整個form提交的數據包括一些簡單的input和一個文件的上傳。下面是代碼:

 

前端JSP代碼:

Java代碼   收藏代碼
  1. < form id ="wfAuditForm" method ="post" enctype ="multipart/form-data">  
  2. < 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.                 });  
 

后台:

Java代碼   收藏代碼
  1. public void save(HttpServletResponse response, HttpServletRequest request, Integer hasUpload,PictureResource pic) {  
  2.      response.setStatus(HttpServletResponse. SC_CONFLICT);  
  3. }  

 

問題是當提交的數據中file標簽里面有值的話(有文件需要上傳),即時后台返回的狀態碼不是200,也會觸發js的success方法。

 

當然第一時間想到的是不是返回的狀態碼不是預期中的,於是使用了firebug對於通信進行了抓包,抓包后發現返回的的確是409(SC_CONFLICT),但是觸發的還是success上面。后來意識到這種問題只有當有文件需要上傳的時候才會發現,因此懷疑form提交的時候返回了兩次response,一次是文件流從客戶端到服務端的過程,一次是真正的數據提交的過程,因此使用了wireshark抓了幾次包,抓出來的報文顯示的確是只返回了一次response(當有文件上傳的時候,會出現一個redirect的報文,這個在后面的博文中會有分析),這個說明跟http的網絡通信及服務端處理沒有關系。

 

問題到底出在什么地方呢?再次回過頭來讀jquery.form.js的代碼,發現這段代碼中有這么一段很可疑:

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

這段代碼的第一個for循環是遍歷form中所有的file標簽,一旦其中的一個file標簽里面有值,就將found設置了true。后面的代碼就是根據found來進行判斷了,如果found為真(有需要上傳的文件)將調用fileUpload方法,否則調用jquery的ajax方法。根據上面的現象描述,問題可能出現在fileUpload方法中。下面我們再看fileUpload方法:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM