• 使用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.         };  
  • 相关阅读:
    百度地图神奇错误-------->不显示
    ListView item点击事件失效问题
    在fragment中使用百度地图
    Fragment学习
    android.view.InflateException: Binary XML file line #246: Error inflating class <unknown>
    Android之获得内存剩余大小与总大小
    Android运行时异常“Binary XML file line # : Error inflating class”
    openfire 服务器配置 php 添加ssl
    《影响力》书中6种原理
    git-03 建立分支
  • 原文地址:https://www.cnblogs.com/jpfss/p/8963731.html
Copyright © 2020-2023  润新知