Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1130 Chapter 24: Debugging and Error Handling Error Notifications During an interactive debugging session, Visual Studio now strives to assist you with informative Error Notifications. These notifications not only report on events such as unhandled exceptions, but also offer context-sensitive troubleshooting tips and next steps for dealing with the situation. Figure 24-15 shows an unhandled NullReferenceException along with the good advice that we might try using the ‘‘new’’ keyword to create an object instance before using it. Oops! Figure 24-15 Edit and Continue (Lack of) Support, or Edit and Refresh Visual Basic 6 was all about developing things quickly, and its most powerful feature was the Edit and Continue feature, which gave you capability to change code during a debugging session without restart- ing the session. In break mode, you could modify code fix bugs and move on. The 2.0 version of the CLR has restored this feature for both C# and Visual Basic. Although this has a large number of developers cheering, unfortunately this feature is not available to ASP.NET developers. In ASP.NET, your assembly is compiled not by Visual Studio, but by the ASP.NET runtime using the same technique it does during a normal Web page request by a browser. To cooperate with the debugger and support Edit and Continue within ASP.NET, a number of fantastically complex modifications t o ASP.NET runtime would have been required by the development team. Rather than including support for this feature, ASP.NET developers can use page recycling. This means that code changes are made during a debugging session, and then the whole page is refreshed via F5, automatically recompiled, and re-executed. Basically, ASP.NET 2.0 includes much improved support for Edit and Refresh, but not for Edit and Continue. Just My Code Debugging A new concept in the .NET 2.0 CLR is called Just My Code debugging. Any method in code can be explicit- ly marked with the new attribute [DebuggerNonUserCode] . Using this explicit technique and a number of 1130 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1131 Chapter 24: Debugging and Error Handling other heuristic methods internal to the CLR, the debugger silently skips over code that isn’t important to the code at hand. You can find the new preference Enable Just My Code in Tools ➪ Options ➪ Debugging. The [DebuggerHidden] attribute is still available in .NET 2.0 and hides methods from the debugger, regardless of the user’s Just My Code preference. The 1.1 attribute [DebuggerStepThrough] tells the debugger to step through, rather than into, any method to which it’s applied; the [DebuggerNonUser Code] attribute is a much more pervasive and complete implementation that works at runtime on dele- gates, virtual functions, and any arbitrarily complex code. Be aware that these attributes and this new user option exist to help you debug code effectively and not be fooled by any confusing call stacks. While these can be very useful, be sure not to use them on your components until you’re sure you won’t accidentally hide the very error you’re trying to debug. Typically these attributes are used for components such as proxies or thin shim layers. Tracepoints Breakpoints by themselves are useful for stopping execution either conditionally or unconditionally. Standard breakpoints break always. Conditional breakpoints cause you to enter an interactive debug- ging session based on a condition. Tracing is useful to output the value of a variable or assertion to the debugger or to another location. If you combine all these features, what do you get? Tracepoints, a new and powerful Visual Studio feature. Tracepoints can save you from hitting breakpoints dozens of times just to catch an edge case variable value. They can save you from covering your code with breakpoints to catch a strange case. To insert a Tracepoint, right-click in the code editor and select Breakpoint ➪ Insert Tracepoint. You’ll get the dialog shown in Figure 24-16. The icon that indicates a breakpoint is a red circle, and the icon for a Tracepoint is a red diamond. Arbitrary strings can be created from the dialog using pseudo-variables in the form of keywords such as $CALLSTACK or $FUNCTION ,aswellasthevaluesofvariablesinscope placed in curly braces. In Figure 24-16, the value of i.FirstName (placed in curly braces) is shown in the complete string with the Debug output of Visual Studio. Client-side Javascript Debugging Excellent client-side Javascript Debugging is new in Visual Studio 2008. If you run an ASP.NET appli- cation in a debugging session in Internet Explorer you’ll need to enable script debugging. If not, you’ll receive a dialog similar to the one in Figure 24-17. After you’ve turned on Script Debugging, try a simple ASPX page with some Javascript that changes the text in a textbox to UPPERCASE when the button is pressed. Listing 24-5: Simple Javascript debugging test ASPX <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/ xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <script type="text/javascript"> function MakeItUpper() Continued 1131 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1132 Chapter 24: Debugging and Error Handling { newText = document.getElementById("TextBox1").value.toUpperCase(); document.getElementById("TextBox1").value = newText; } </script> </head> <body> <form id="form1" runat="server"> <div> <input type="button" id="Button1" value="Upper" onclick="javascript:MakeItUpper()" /> <input type="text" id="TextBox1" runat="server"/> </div> </form> </body> </html> Figure 24-16 Put a breakpoint on one of the lines of client-side Javascript. Note that this is code that runs in the browser, not on the Web server. Start a debugging session with the page from Listing 24-5. Visual Studio will break at that point, as shown in Figure 24-18. The Javascript debugger in Visual Studio 2008 supports variable tooltips, visualizers, call stacks, locals, watches, and all the features you’re used to when debugging .NET-based languages. 1132 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1133 Chapter 24: Debugging and Error Handling Figure 24-17 Figure 24-18 1133 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1134 Chapter 24: Debugging and Error Handling Notice in the figure that Default.aspx has the word [dynamic] listed on the tab, indicating that this isn’t the same default.aspx that was edited earlier; you can see that default.aspx listed on t he final tab. Rather, this is the dynamically generated default.aspx that was delivered to the browser, including ViewState and other generated elements. Dynamically generated documents and scripts appear during the debug session in the Solution Explorer. This rich debugging support on the client side makes creating today’s Javascript-heavy AJAX a pplica- tions much easier. SQL Stored Proc Debugging Database projects are file-based projects that let you manage and execute database queries. You can add your existing SQL scripts to the project or create new ones and edit them within Visual Studio. Database projects and SQL debugging are not available in the Express or Standard versions of Visual Studio. They are available only in the Professional or Team Edition Visual Studio SKUs/versions. When debugging database applications, you can’t use Step Into (F11) to step between code in the appli- cation tier into the code in SQL Server 2005 (be it T-SQL or CLR SQL). However, you can set a breakpoint in the stored procedure code and use Continue (F5) to execute code to that set break point. When debugging SQL on SQL Server 2005, be aware of any software or hardware firewalls you may be running. Windows XP SP2’s software firewall will warn you what you’re trying to do. Be sure to select ‘‘unblock’’ in any warning dialogs to ensure that SQL Server 2005 and Visual Studio can communicate. If you are using a SQL account to connect to the SQL Server, make sure the Windows User Account you run Visual Studio under is also an administrator on the SQL Server machine. You can add accounts to SQL Server’s sysadmin privilege using the SQL command sp_addsrvrolemember ’Domain \ Name’, ’sysadmin’ . Of course, never do this in production; and better yet, do your debugging on a machine with everything installed locally. If you’re using the NT Authentication model on the SQL Server 2005, make sure that account has per- missions to run the sp_enable_sql_debug stored procedure. You can give account access to this stored procedure by using the SQL commands CREATE USER UserName FOR LOGIN ’Domain \ Name’ followed by GRANT EXECUTE ON sp_enable_sql_debug TO UserName . This creates a SQL user that is associated directly with a specific Windows User and then explicitly grants permissions to debug TSQL to that user. On SQL Server 2000, the user must have access to the e xtended stored procedure sp_sdidebug . For slightly older installations such as Windows 2000 and Windows NT 4, or if you are using SQL 2000, be sure to visit MSDN for the latest details and tools in this space. The MSDN URL for debugging SQL Server is http://msdn2.microsoft.com/library/zefbf0t6 . Exception and Error Handling When an exception occurs in your ASP.NET application code, you can handle it in a number of ways, but the best approach is a multi-pronged one: ❑ Catch what you expect: ❑ Use a Try/Catch around error-prone code. This can always catch specific exceptions that you can deal with, such as System.IO.FileNotFoundException 1134 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1135 Chapter 24: Debugging and Error Handling ❑ Rather than catching exceptions around specific chunks of code at the page level, consider using t he page-level error handler to catch specific exceptions that might happen anywhere on the page. ❑ But prepare for unhandled exceptions: ❑ Set the Page.Error property if a specific page should show a specific error page for any unhandled exception. This can also be done using the < %@ Page > directive or the code behind the property. ❑ Have default error pages for 400 and 500 errors set in your web.config . ❑ Have a boilerplate Application_OnError handler that takes into consideration both spe- cific exceptions that you can do something about, as well as all unhandled exceptions that you may want logged to either the event log, a text file, or other instrumentation mechanism. The phrase unhandled exception may be alarming, but remember that you don’t do anyone any good catching an exception that you can’t recover from. Unhandled exceptions are okay if they are just that — exceptional. For these situations, rely on global exception handlers for logging and friendly error pages that you can present to the user. Why try to catch an exception by adding code everywhere if you can catch and log exceptions all in one place? A common mistake is creating a try/catch block around some arbitrary code and catching the least specific exception type — System. Exception . A rule of thumb is, don’t catch any exception that you can’t do anything about. Just because an exception can be thrown by a particular method doesn’t mean you have to catch it. It’s exceptional, remember? Also, there are exception handlers at both the page and the application level. Catch exceptions in these two centralized locations rather than all over. Handling Exceptions on a Page To handle exceptions at a page level, override the OnError method that System.Web.UI.Page inherits from the TemplateControl class (see Listing 24-5). Calling Server.GetLastError gives you access to the exception that just occurred. Be aware that a chain of exceptions may have occurred, and you can use the ExceptionGetBaseException method to return the root exception. Listing 24-6: Page-level error handling VB Protected Overrides Sub OnError(ByVal e As System.EventArgs) Dim AnError As System.Exception = Server.GetLastError() If (TypeOf AnError.GetBaseException() Is SomeSpecificException) Then Response.Write("Something bad happened!") Response.StatusCode = 200 Server.ClearError() Response.End() End If End Sub Continued 1135 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1136 Chapter 24: Debugging and Error Handling C# protected override void OnError(EventArgs e) { System.Exception anError = Server.GetLastError(); if (anError.GetBaseException() is SomeSpecificException) { Response.Write("Something bad happened!"); Response.StatusCode = 200; Server.ClearError(); Response.End(); } } Handling Application Exceptions The technique of catching exceptions in a centralized location can be applied to error handling at the application level in Global.asax , as shown in Listing 24-6. If an exception is not caught on the page, the web.config is checked for an alternate error page; if there isn’t one, the exception bubbles up to the application and your user sees a complete call stack. Listing 24-7: Application-level error handling VB Protected Sub Application_Error(sender as Object, ByVal e As System.EventArgs) Dim bigError As System.Exception = Server.GetLastError() ’Example checking for HttpRequestValidationException If (TypeOf bigError.GetBaseException() Is HttpRequestValidationException) Then System.Diagnostics.Trace.WriteLine(bigError.ToString) Server.ClearError() End If End Sub C# protected void Application_Error(Object sender, EventArgs e) { System.Exception bigError = Server.GetLastError(); //Example checking for HttpRequestValidationException if(bigError.GetBaseException() is HttpRequestValidationException ) { System.Diagnostics.Trace.WriteLine(bigError.ToString()); Server.ClearError(); } } Unhandled application errors turn into HTTP Status Code 500 and display errors in the browser. These errors, including the complete callstack and other technical details, may be useful during development, but are hardly useful at production time. Most often, you want to create an error handler (as shown previously) to log your error and to give the user a friendlier page to view. 1136 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1137 Chapter 24: Debugging and Error Handling If you ever find yourself trying to catch exceptions of type System.Exception ,takea look at the code to see whether you can avoid it. There’s almost never a reason to catch such a non-specific exception, and you’re more likely to swallow exceptions that can provide valuable debugging. Check the API documentation for the framework method you are calling — a section specifically lists what exceptions an API call might throw. Never rely on an exception occurring to get a standard code path to work. Http Status Codes Every HttpRequest results in an HttpResponse , and every HttpResponse includes a status code. The following table describes 11 particularly interesting HTTP status codes. Status Code Explanation 200 OK Everything went well. 301 Moved Permanently Reminds the caller to use a new, permanent URL rather than the one he used to get here. 302 Found Returned during a Response.Redirect. This is the way to say ‘‘No, no, look over here right now.’’ 304 Not Modified Returned as the result of a conditional GET when a requested document hasn’t been modified. It is the basis of all browser-based caching. An HTTP message-body must not be returned when using a 304. 307 Temporary Redirect Redirects calls to ASMX Web services to alternate URLs. Rarely used with ASP.NET. 400 Bad Request Request was malformed. 401 Unauthorized Request requires authentication from the user. 403 Forbidden Authentication has failed, indicating that the server understood the requests but cannot fulfill it. 404 Not Found The server has not found an appropriate file or handler to handle this request. The implication is that this may be a temporary state. This happens in ASP.NET not only because a file cannot be found, but also because it may be inappropriately mapped to an IHttpHandler that was not available to service the request. 410 Gone The equivalent of a permanent 404 indicating to the client that it should delete any references to this link if possible. 404s usually indicate that the server does not know whether the condition is permanent. 1137 Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1138 Chapter 24: Debugging and Error Handling Status Code Explanation 500 Internal Server Error The official text for this error is ‘‘The server encountered an unexpected condition which prevented it from fulfilling the request,’’ but this error can occur when any unhandled exception bubbles all the way up to the user from ASP.NET. Any status code greater than or equal to 400 is considered an error and, unless you configure otherwise, the user will likely see an unfriendly message in his browser. If you have not already handled these errors inside of the ASP.NET runtime by checking their exception types, or if the error occurred outside of ASP.NET and you want to show the user a friendly message, you can assign pages to any status code within web.config , as the following example shows: <customErrors mode ="On" > <error statusCode ="500" redirect ="FriendlyMassiveError.aspx" /> </customErrors> After making a change to the customer errors section of your web.config , make sure a page is available to be shown to the user. A classic mistake in error redirection is redirecting the user to a page that will cause an error, thereby getting him stuck in a loop. Use a great deal of care if you have complicated headers or footers in your application that might cause an error if they appear on an error page. Avoid hitting the database or performing any other backend operation that requires either user authorization or that the user’s session be in any specific state. In other words, make sure that the error page is a reliable standalone. Any status code greater than or equal to 400 increments the ASP.NET Requests Failed performance counter. 401 increments Requests Failed and Requests Not Authorized. 404 and 414 increment both Requests Failed and Requests Not Found. Requests that result in a 500 status code increment Requests Failed and Requests Timed Out. If you’re going to return status codes, you must realize their effects and their implications. Summary This chapter examined the debugging tools available to you for creating robust ASP.NET applications. A successful debugging experience includes not only interactive debugging with new features such as datatips, data visualizers, and error notifications, but also powerful options around configurable tracing and logging of information. Remote debugging is easier than ever with ASP.NET, and the capability to write and debug ASP.NET pages without installing IIS removes yet another layer of complexity from the development process. Visual Studio and its extensible debugging mechanisms continue to be expanded by intrepid bloggers and enthusiasts, making debugging even less tedious than it has been in the past. 1138 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1139 File I/O and Streams Although most of this book concentrates specifically on learning and using the features of ASP.NET 3.5, .NET provides an enormous amount of additional functionality in other areas of the Base Class Library (BCL). This chapter examines a few of the common base classes that you can use to enhance your ASP.NET applications. First, you look at using the frameworks System.IO namespace to man- age files on the local file system. Next, you explore how to use the various Stream classes within the framework to read from and write different data formats to memory and the local file system. Finally, you learn how to use the .NET Framework to communicate with other computers across the Internet using common protocols such as HTTP and FTP. A Word about I/O Security Although this chapter is not specifically about ASP.NET security, you need to understand the impact of local system security on what the ASP.NET Worker Process is allowed to do inside of the IO namespace. Remember that generally, when your code is executed by IIS, it executes under the context of the ASP.NET Worker Process user account (ASPNET) and, therefore, your application may be restricted by that account’s security rights. For example, by default, the ASP.NET Worker Process does not have rights to write to the local disk. The two main areas that you should look at to get a very basic understanding of the impact of security on an application are impersonation and user account ACLs. ASP.NET security is discussed thoroughly in Chapter 18. Additionally, this chapter demonstrates how to use classes in the BCL to delete files and directories and to modify the permissions of directories and files. Recognize that it is entirely possible to permanently delete important data from your hard drive or change the permissions of a resource, which would result in you losing the ability to access the resource. Be very careful when using these classes against the file system. . debugging .NET- based languages. 1 132 Evjen c24.tex V2 - 01/28/2008 3: 40pm Page 1 133 Chapter 24: Debugging and Error Handling Figure 24-17 Figure 24-18 1 133 Evjen c24.tex V2 - 01/28/2008 3: 40pm. using the ‘‘new’’ keyword to create an object instance before using it. Oops! Figure 24- 15 Edit and Continue (Lack of) Support, or Edit and Refresh Visual Basic 6 was all about developing things. Studio. Client-side Javascript Debugging Excellent client-side Javascript Debugging is new in Visual Studio 2008. If you run an ASP. NET appli- cation in a debugging session in Internet Explorer you’ll need