时无法修改Header。所以我们必须使用一个方法来“欺骗”ScriptManager。
目前使用的解决方案是,我们在POST数据之前在页面中隐藏的输入元素(<input type="hidden" />)中放入一个特定的标记,然后我们开发的服务器端组件(我把它叫做UpdatePanelFileUplaod)会在它的Init阶段(OnInit方法)中在Request Body中检查这个标记,然后使用反射来告诉ScriptManager目前的请求为一个异步请求。
但是事情并不像我们想象的那么简单,让我们在写代码之前来看一个方法:
internal sealed class PageRequestManager
{
//
internal void OnInit()
{
if (_owner.EnablePartialRendering && !_owner._supportsPartialRenderingSetByUser)
{
IHttpBrowserCapabilities browser = _owner.IPage.Request.Browser;
bool supportsPartialRendering =
(browser.W3CDomVersion >= MinimumW3CDomVersion) &&
(browser.EcmaScriptVersion >= MinimumEcmaScriptVersion) &&
browser.SupportsCallback;
if (supportsPartialRendering)
{
supportsPartialRendering = !EnableLegacyRendering;
}
_owner.SupportsPartialRendering = supportsPartialRendering;
}
if (_owner.IsInAsyncPostBack)
{
_owner.IPage.Error += OnPageError;
}
}
}
上面这段代码会在ScriptManager的OnInit方法中被调用。请注意最后加粗部分的代码,“_owner”变量是当前页面上的ScriptManager。在页面收到一个真正的异步回送之后,PageRequestManager会响应页面的Error事件,并且将错误信息用它定义的格式输出。如果我们只是修改了ScriptManager的私有field,那么如果在异步回送时出现了一个未捕获的异常,那么页面就会输出客户端未知的内容,导致在客户端解析失败。所以我们必须保证这种情况下的输出和真正的异步回送是相同的,于是我们就可以使用以下的做法来解决错误处理的问题。
internal static class FileUploadUtility
{
public static bool IsInUploadAsyncPostBack(HttpContext context)
{
string values = context.Request.Params.GetValues("__UpdatePanelUploading__");
if (values == null) return false;
foreach (string value in values)
{
if (value == "true")
{
return true;
}
}
return false;
}
}
[PersistChildren(false)]
[ParseChildren(true)]
[NonVisualControl]
public class UpdatePanelFileUpload : Control
{
// ScriptManager members;
private readonly static FieldInfo s_isInAsyncPostBackFieldInfo;
private readonly static PropertyInfo s_pageRequestManagerPropertyInfo;
// PageRequestManager members;
private readonly static MethodInfo s_onPageErrorMethodInfo;
static UpdatePanelFileUpload()
{
// Omitted: Initializing of the static members for reflection;
}
private bool m_pageInitialized = false;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// Omitted: Initializing UpdatePanelFileUpload control on the page;
this.IsInUploadAsyncPostBack = FileUploadUtility.IsInUploadAsyncPostBack(this.Context);
if (this.IsInUploadAsyncPostBack)
{
s_isInAsyncPostBackFieldInfo.SetValue(ScriptManager.GetCurrent(this.Page), true);
this.Page.Error += (sender, ea) =>
{
s_onPageErrorMethodInfo.Invoke(
this.PageRequestManager, new object { sender, ea });
};
}
}
public bool IsInUploadAsyncPostBack { get; private set; }
private object m