让UpdatePanel支持上传文件:解决当页面显式设置document.domain时提示的500错误

    最近在做一个项目,需要在UpdatePanel中上载文件,在网络上找了一下,发现有老赵写的AjaxUploadHelper,一开始工作的很好;开发时使用localhost来进行测试的,一切正常;但部署到正式环境后,噩梦开始了,所有在UpdatePanel内的异步操作都不正常,都提示如下信息:

Sys.WebForms.PageRequestManagerServerErrorException: An unknown error occurred while processing the request on the server. The status code returned from the server was: 500

一开始以为是开发环境与正式环境不同导致服务器端输出HTTP 500的错误呢,但怎么也找不到服务器上有关异常的日志信息,后来偶然把上载文件的控件设置为Visible = false,竟然不报这个错了,一切正常;研究了一下AjaxUploadHelper这个控件,是使用iframe来提交的,而不幸的是我们的系统需要跟别的老系统进行页面上的交互,所以系统的BasePage统一输出了一段脚本来设置document.domain,以让2个子域的系统进行交互:

1try {
2  document.domain = 'devfx.net';
3}
 catch(e) {;}

 但是,AJAX异步通过iframe提交,输出的结果是不会有这段脚本的,这导致主页面的document.domain = 'devfx.net',而iframe却是真正域local.devfx.net,所以AjaxUploadHelper内的脚本获取不了iframe的内容,浏览器报Access Denied,AjaxUploadHelper的脚本把这个错误统一封装成500的错误了,可以看一下AjaxFileUploadHelper.js的代码:

        try
        
{    
            
var f = iframe.contentWindow.__f__;
            
var responseData = f ? this._parseScriptText(f.toString()) : 
                
this._parsePreNode(iframe.contentWindow.document.body.firstChild);
                
            
if (responseData.indexOf("\r\n"< 0 && responseData.indexOf("\n"> 0)
            
{
                responseData 
= responseData.replace(/\n/g, "\r\n");
            }

                
            
this._responseData = responseData;
            
this._statusCode = 200;
            
this._responseAvailable = true;
        }

        
catch (e)
        
{
            
this._statusCode = 500;
            
this._responseAvailable = false;
        }


i,害人哪。。。。
既然知道了是由于跨域产生的,解决起来就比较容易了。
修改AjaxUploadHelper,增加属性ClientDocumentDomain,用以设置iframe的document.domain:
        private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
        
{
            AjaxFileUploadUtility.WriteScriptBlock(
this.Page.Response, true);

            StringBuilder sb 
= new StringBuilder();
            HtmlTextWriter innerWriter 
= new HtmlTextWriter(new StringWriter(sb));
            renderPageCallbackMethodInfo.Invoke(
this.PageRequestManager, new object[] { innerWriter, pageControl });

            writer.Write(sb.Replace(
"*/""*//*").ToString());

            AjaxFileUploadUtility.WriteScriptBlock(
this.Page.Response, false);

            
if(!string.IsNullOrEmpty(this.ClientDocumentDomain)) {
                
string domainScript = string.Format("<script type='text/javascript' language='javascript'>try {{ document.domain = '{0}'; }} catch(e) {{;}}</script>"this.ClientDocumentDomain);
                writer.Write(domainScript);
            }

        }

这下似乎一切正常了。

上面的修改只是针对AjaxUploadHelper而改的。修改后的代码包:New AjaxFileUploadHelper

到目前为止,老赵又发了更新:让UpdatePanel支持上传文件,这次使用了jquery以及插件jquery.form.js,把原来自己创建iframe的工作交由给jquery.form来处理,而且针对的framework是3.5的,(老赵发的代码包缺了好多脚本,害我好找!!);我以为新版本能把这个问题解决,可惜还是没有;我以为按修改AjaxUploadHelper方法就可以了,尝试了3天到目前为止还没有很完美的解决方案。主要的障碍在AJAX异步回调后,输出的是Content-Type是text/plain,如果这个时候输出脚本来设置domain,似乎任何脚本都不会被执行(好像浏览器把输出仅当成文本了),自然iframe的document.domain还是不对的,所以还是会报500的假错误(貌似AjaxUploadHelper控件却是可以的,不解了);如果完全改成AjaxUploadHelper的操作方式,不知道会不会解决;等有空的时候在深入研究研究。
原文地址:https://www.cnblogs.com/R2/p/1202047.html