Asynchronous error handling change in ASP.NET AJAX 3.5
There is a subtle change in the .NET 3.5 release of the Microsoft Ajax Library that might catch some developers by surprise when handling asynchronous postback errors. The change can be found at the end of the endPostBack function of the PageRequestManager object. In the first release of the Ajax library, if an error was not handled on the client; an alert box was used to convey to the user the error message:
ASP.NET AJAX 1.0 (MicrosoftAjaxWebForms.debug.js):
1: function Sys$WebForms$PageRequestManager$_endPostBack(error, response) {
2: this._processingRequest = false;
3:
4: this._request = null;
5: this._additionalInput = null;
6:
7: var handler = this._get_eventHandlerList().getHandler("endRequest");
8: var errorHandled = false;
9: if (handler) {
10: var eventArgs = new Sys.WebForms.EndRequestEventArgs(error, this._dataItems, response);
11: handler(this, eventArgs);
12: errorHandled = eventArgs.get_errorHandled();
13: }
14: this._dataItems = null;
15: if (error && !errorHandled) {
16: alert(error.message);
17: }
18: }
1: function Sys$WebForms$PageRequestManager$_endPostBack(error, response) {
2: if (this._request === response.get_webRequest()) {
3: this._processingRequest = false;
4: this._additionalInput = null;
5: this._request = null;
6: }
7: var handler = this._get_eventHandlerList().getHandler("endRequest");
8: var errorHandled = false;
9: if (handler) {
10: var eventArgs = new Sys.WebForms.EndRequestEventArgs(error, this._dataItems, response);
11: handler(this, eventArgs);
12: errorHandled = eventArgs.get_errorHandled();
13: }
14: if (!this._processingRequest) {
15: this._dataItems = null;
16: }
17: if (error && !errorHandled) {
18: throw error;
19: }
20: }
So what does this mean to code you've written for ASP.NET 2.0 that has been migrated to ASP.NET 3.5? Well, if you haven't handled the endRequest client-side event after an async postback then the user is now going to experience a runtime error, whereas they would have simply experienced an alert box with the error message before:
Here is a simple example of how to reproduce this behavior in ASP.NET 3.5:
HTML:
1: <asp:ScriptManager ID="ScriptManager1" runat="server" />
2: <div>
3: <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
4: <ContentTemplate>
5: <asp:Button ID="ThrowError" runat="server" OnClick="ThrowError_OnClick" Text="Throw Error" />
6: </ContentTemplate>
7: </asp:UpdatePanel>
8: </div>
Code behind:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: ScriptManager sm = ScriptManager.GetCurrent(this.Page);
4: sm.AsyncPostBackError += new EventHandler<AsyncPostBackErrorEventArgs>(ScriptManager1_OnAsyncPostBackError);
5: sm.AllowCustomErrorsRedirect = false;
6: }
7:
8:
9: protected void ScriptManager1_OnAsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
10: {
11: ScriptManager.GetCurrent(this.Page).AsyncPostBackErrorMessage = "Please try that again.";
12: }
13:
14: protected void ThrowError_OnClick(object sender, EventArgs e)
15: {
16: throw new InvalidOperationException("Throw error!");
17: }
Lastly, here is an example of how to handle the endRequest event thrown by the PageRequestManager to avoid the runtime error:
1: <script type="text/javascript">
2:
3: Sys.WebForms.PageRequestManager.getInstance().add_endRequest(onEndRequest);
4:
5: function onEndRequest(sender, args) {
6: var error = args.get_error();
7: if (error != null) {
8:
9: // Set the error handled flag to avoid a runtime error
10: // reaching the user.
11: args.set_errorHandled(true);
12:
13: // Remove the error name from the message
14: var msg = error.message.replace("Sys.WebForms.PageRequestManagerServerErrorException: ", "");
15: alert(msg);
16: }
17: }
18:
19: </script>
In the example, the message name is removed so that the user will only view the intended postback error message set from the code-behind. But more importantly, is the ability to take control of how the error is handled with the handler for the endRequest event.
For a more in-depth example of client-side events during an async postback, check out this post: Client-side event viewer.