日期:2014-05-16 浏览次数:20499 次
周五同事遇到一个很奇怪的问题,调到下班,虽然问题解决了,但是不知道问题的具体原因,回来翻了翻代码,才发现症结所在,下面就分享出来,供遇到同样问题的同行们参考:
?
先把问题描述一下,做的功能是使用ajax向后台来提交数据,为了向用户进行很好的错误提示,后台中将出现错误时的错误原因返回给前端,前端使用jquery.form.js的ajaxsubmit来提交数据,并在success方法中提示“操作成功”,在error方法中提示错误原因。整个form提交的数据包括一些简单的input和一个文件的上传。下面是代码:
?
前端JSP代码:
< form id ="wfAuditForm" method ="post" enctype ="multipart/form-data"> < input type ="file" name ="posterUrlUploadPath" id ="posterUrlUploadPath" class ="fileUpload" title ="上传图片" />
?
前端JS代码:
$("#wfAuditForm").ajaxSubmit({ type: 'post', url: "data/resource/picture/save" , success: function(data){ alert( "success"); $( "#wfAuditForm").resetForm(); }, error: function(XmlHttpRequest, textStatus, errorThrown){ alert( "error"); } });?
后台:
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的代码,发现这段代码中有这么一段很可疑:
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方法:
// 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 id="' + id + '" 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