Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 76 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
76
Dung lượng
1,98 MB
Nội dung
public static void SendMail(string Message, Exception Ex) { using (MailMessage msg = new MailMessage(“website@wroxunited.net”, “admin@wroxunited.net”)) { msg.Subject = “WroxUnited.net Web Site Error”; if (Ex == null) msg.Body = “There was an error on the website”; else msg.Body = Ex.ToString(); SmtpClient client = new SmtpClient(“MyMailServer”); client.UseDefaultCredentials = true; client.Send(msg); } } Two classes are in use here. The MailMessage class defines the message to be sent — the constructor sets the “from” and “to” addresses for the message, and the Subject and Body properties set the subject line and the contents. The second class is the SmtpClient, the constructor of which defines the name of the mail server. Setting UseDefaultCredentials to true allows the ASP.NET code to connect to the server using Windows network credentials. Finally, the Send method actually sends the mail. You can configure some of these properties in the web configuration file, Web.config, which is where you can also set authentication settings if they are required for connection to the mail server: <configuration xmlns=”http://schemas.microsoft.com/.NetConfiguration/v2.0”> <system.net> <mailSettings> <smtp deliveryMethod=”Network”> <network defaultCredentials=”False” host=”MyMailServer” password=”MyPassword” port=”25” userName=”MyUserName” from=”website@wroxunited.net”/> </smtp> </mailSettings> </system.net> </configuration> The properties for configuring mail are fairly simple. Setting defaultCredentials to false ensures that the user name ( userName) and password (password) specified are used to connect to the e-mail server ( host), and from sets the e-mail address of whom the e-mail is from. The port number has to do with TCP networking—e-mail uses port number 25, and you don’t need to know any more about ports apart from that number. Sending mail isn’t just for notifying administrators of exceptions, and you can use it for all sorts of things. The security framework will use these settings when it sends forgotten passwords if users request them. 577 Dealing with Errors 18_042583 ch15.qxd 4/4/06 2:52 PM Page 577 You could use the e-mail code instead of, or in conjunction with, the logging to a file. For example, you could have the following: Tools.Log(“My error message”, SqlEx); Tools.SendMail(“My error message”, SqlEx); This would perform both actions, but it includes repeated code—the error message. An alternative would be to add the e-mail code into the Log method, but that would always send the e-mail, which might not be required. A better option might be to only send an e-mail if required, perhaps adding another parameter to the Log method to indicate if the e-mail is to be sent: Tools.Log(“My error message”, SqlEx, true); The Log method could then have the following code: public static void Log(string Message, Exception Ex, bool SendEmailMessage) { if (SendEmailMessage) SendEmail(Message, Ex); // rest of logging code } This gives a combination of the passive reporting to a log file, and the active, which lets the administra- tor know of problems as they occur. Raising Exceptions You’ve seen that you can trap exceptions, but you can also raise your own exceptions. One use for this is that you can use the same exception handling mechanism to deal with custom errors as you use for .NET errors. However, this comes with a warning, in that you should still adhere to the rule of only using exceptions for exceptional circumstances. If you can handle the problem gracefully without using exceptions, you should do so. To raise an exception, you use the Throw statement. For example: throw new Exception(“exception description”); You can also pass in an underlying exception: throw new exception(“exception description”, ex); If you are within a Catch block, you can also re-throw the existing exception by just calling the Throw statement on its own. You’ll see how this can be used a little later when handling exceptions globally is discussed. 578 Chapter 15 18_042583 ch15.qxd 4/4/06 2:52 PM Page 578 Exceptions Best Practices Using exceptions is good practice, but the following rules should be adhered to when dealing with exceptions: ❑ You should only catch an exception if you actually expect the exception. This doesn’t mean that it will happen, but that it could. A database failure is a good example, because these aren’t unheard of. If you can understand why an exception would occur and you know how to deal with it, then that’s a good case for catching it. ❑ Dealing with the exception doesn’t mean that you know how to cure it, but that something can be done. For example, in the Checkout page modified earlier in the chapter, catching SqlException was necessary to allow the use of transactions so the database wouldn’t be left in an inconsistent state. That is dealing with the exception, even if there is nothing that can be done about the underlying problem. ❑ As a general rule, it’s a good idea to avoid catching only the base Exception. Because this is the base class for all exceptions, it’s not narrow enough in its focus. ❑ If you are performing database work, catch the appropriate exception ( SqlException for SQL Server). Global Exception Handling Handling exceptions where they happen is both good and bad. In the Checkout page, the exception had to be dealt with locally because of the transaction, but in many cases, you might want some form of cen- tralized exception handling. You also might want some way to handle exceptions not trapped elsewhere. The Checkout page is again a good example, because there is handling for SqlException, but not for anything else. What happens if some other exception occurs? This is an important point because it really isn’t sensible to put Try Catch around every piece of code just in case an exception might occur. In fact, that would be bad practice because it would make the code hard to read and isn’t required. The way global exception handling is managed is with the Global.asax file, which contains code for the application. In this case, the term application has a special meaning, because code in Global.asax responds to application-level events. These are events that are raised at specific points during the run- ning of the application, events that are raised by ASP.NET. Global.asax is a code-only page, and has no user interaction. Several events are contained in the Global.asax page: ❑ Application_Start: Raised when the application first starts. This will be when the first user accesses the site and should be used to set any initial start conditions. ❑ Application_End: Raised when the application stops. ❑ Session_Start: Raised when a user starts a session. This will be when the user starts accessing the site for the first time, and will include the time when a user closes the browser window and opens it again. 579 Dealing with Errors 18_042583 ch15.qxd 4/4/06 2:52 PM Page 579 ❑ Session_End: Raised when a user session ends. This isn’t when the browser window is closed, because sessions have a timeout — if there is no user activity within that time, the session ends. ❑ Application_Error: Raised when an unhandled error occurs. ❑ Profile_OnMigrateAnonymous: Raised when an anonymous user logs in, and allows migra- tion of any Profile properties. (The Profile was covered in Chapter 11.) As you can see, the event you’re interested in is the Application_Error event, which is where you can add code to centrally handle untrapped exceptions. You see how the Application_Error event can be used in the following Try It Out. Try It Out Handling Global Errors 1. Using Windows Explorer, navigate to the web site directory, C:\BegASPNET2\Chapters\ Begin\Chapter15\WroxUnited , and have a look at WroxUnited.log. 2. In the Wrox United application, open the global.asax file. 3. Add the following code to the Application_Error event procedure: Exception ex = Server.GetLastError(); Tools.Log(“An unhandled error was caught by Application_Error”, ex); 4. Save the file. 5. Open checkout.aspx.cs and move to the Wizard1_FinishButtonClick event. 6. Comment out the code that logs the error and replace it with the following: throw; 7. To ensure that the exception is seen in practice, there actually needs to be some error, so you’ll force one by making the SQL statement incorrect. Change the SQL statement that inserts into the Orders table to insert into no_Orders: cmd.CommandText = “INSERT INTO no_ORDERS(MemberName ”; 8. Save the file and run the application. 9. If there are no items in your cart, go to the Wrox United shop and add some items. Otherwise, proceed to the Checkout page. Step through the checkout wizard. After clicking the Finish but- ton, you’ll be switched back to VWD stating that an error has occurred. Press F5 to continue. 10. Using Windows Explorer, navigate to the web site directory, C:\BegASPNET2\Chapters\ Begin\Chapter15\WroxUnited , and check the WroxUnited.log file (or you can press the refresh button in the Solution Explorer, and the new file will appear). You’ll notice that the exception has been logged. Check the last error and see that it states that an error was caught by Application_Error. Also notice that the next line states that an HttpUnhandledException was thrown, followed by details of the SqlException. 11. Delete WroxUnited.log and switch back to the Checkout.aspx.cs code. 12. Change the throw statement to this: throw new Exception(“An error occurred while creating the order”, SqlEx); 580 Chapter 15 18_042583 ch15.qxd 4/4/06 2:52 PM Page 580 13. Run the application again and follow the same procedure to generate the error. 14. Open WroxUnited.log again and look at the details. First there is the HttpUnhandledException, then Exception with the text you added, and then the SqlException. How It Works The first thing to understand is that the Application_Error in global.asax is raised when any unhandled error occurs. In this event, in your code you need to find out what the error was that caused the event to be raised, and for that you use the GetLastError method of the Server object. This returns an exception object, which is passed into the Log method to log the error. So if you had a Try Catch block around your code, how was it that you got to the Application_Error event? It’s because you re- threw the error using the Throw statement — although you handled the initial SqlException, the re- thrown exception wasn’t handled. The important thing to note about what happened is that the exception is wrapped in another exception — an HttpUnhandledException. All exceptions you get from within Application_Error will be like this. The actual SqlException is shown because in the Log method, you used ToString to write out all of the details of the exception. If you didn’t want the HttpUnhandledException shown, you could use the InnerException property of the exception: Exception ex = Server.GetLastError().InnerException; When the exception is logged, now it would only show the actual exception that was unhandled. In the second case, you used the following: throw new Exception(“An error occurred while creating the order”, SqlEx); This throws a new Exception, but passes into that the actual exception that caused the problem. So you’ve wrapped the original exception within your own — a useful technique if you need to store more details than are available in the original exception. Here you are just detailing that the problem arose when an order was being created. Don’t correct the SQL statement. You’ll need the incorrect statement in a later exercise when you look at debugging. In general, using this simple code in Application_Error and logging exceptions to a file means that you always have details of problems that occur during the normal running of a site. You can use the stack trace (more on this later) to see exactly where the problem was, and you don’t have to rely on users telling you what they think the problem was (the two very rarely match). Custom Error Pages One problem with the error handling code shown so far is that the user still sees a confusing message. Ideally, you’d like to present the user with something less shocking than a stack trace (a list of the meth- ods called so far, which you’ll look at later), for two very good reasons. First, a stack trace is not what 581 Dealing with Errors 18_042583 ch15.qxd 4/4/06 2:52 PM Page 581 users need to see — if something has gone wrong, then they need to see a clear description, explaining that it wasn’t their problem, and that something is being done about it. Second, showing a stack trace gives away a lot of information about your site, details that can be used by hackers. Even if your site is secure, they could cause unnecessary slowdowns as they try to hack the site. In addition to exceptions, there are other types of errors that aren’t nice for a user to see. For example, what if you rename a page but don’t update the links to it, or perhaps the user types in the wrong name for a page? In these cases, you’d see the 404 — the error number that indicates a page could not be found. ASP.NET applications can be configured to redirect the user to other pages, depending on the type of error. Configuring Custom Error Pages Configuration of custom error pages is done in the Web.config file using the customErrors section. For example: <customErrors mode=”On” defaultRedirect=”customError.aspx”> <error statusCode=”404” redirect=”missingPage.aspx” /> </customErrors> The mode attribute can be one of the following: ❑ Off: The ASP.NET error details are always shown, even if a custom error page exists. ❑ On: The custom error is always shown, and the ASP.NET error details are never shown. ❑ RemoteOnly: The ASP.NET error details are only shown to local users, meaning users logged on locally to the machine. For remote users (everyone else using the site), one of two things is shown: a default page telling the user that an error has occurred, but without showing any error details, or a custom error page if one exists. The values of On or RemoteOnly should be used for a live site, whereas Off can be used for debugging purposes. The defaultRedirect attribute defines the page that is shown if an unhandled error occurs. The error element details specific errors and the page to redirect to if that error occurs. In this case, the statusCode is 404, which means a missing page, so the user will be redirected to missingPage.aspx if the page they are looking for cannot be found. This enables you to have detailed pages for individual errors, so you can help the user correct the problem. The missing page example could explain that the page cannot be found and perhaps get users to check that they typed the correct URL. In the following Try It Out, you create your own custom error page. Try It Out Custom Error Pages 1. In the Wrox United application for the chapter, open Web.config and add the following within the <system.web> section: <customErrors mode=”On”> <error statusCode=”404” redirect=”missingPage.aspx” /> </customErrors> 582 Chapter 15 18_042583 ch15.qxd 4/4/06 2:52 PM Page 582 2. Save the file. 3. Create a new Web Form called missingPage.aspx, making sure that the code isn’t placed in a separate file, but that you pick the site.master file for the Master page. 4. Within the <asp:Content> controls, add the following text: We’re sorry but the page you were looking for cannot be found. It’s probably hiding behind the couch. We’ll tell the Web site administrator to go and fetch it. 5. Save the file and run the application. 6. Navigate to a page that doesn’t exist — perhaps abc.aspx. You’ll need to type this page into the address bar of the browser. Notice that the text you entered is displayed rather than the normal message for a missing page. How It Works The working of this is quite simple, because when custom errors are enabled, ASP.NET intercepts the errors. What it does depends on how you’ve configured the customErrors section. In this case, the mode has been set to On, which means that custom errors will always be shown—this is required because you are logged on to the machine locally, so remoteOnly wouldn’t work. You’ve also configured a custom page for the statusCode of 404, so whenever a page cannot be found, ASP.NET doesn’t show the normal error message but instead redirects to the custom error page. This technique makes your site friendlier to use, which means that if an error does occur, the user isn’t left with a frightening error message, but is presented with something more reassuring. Also, because this is an ASP.NET page, you could make the error message more helpful. For example, you could check the name of the file the user was looking for and see if something similar exists on the site, perhaps by look- ing up the pages in the SiteMap or from a database. You could then either take the user to the nearest matching page, or present them with a list of possible matches. Error pages can be combined with logging to give very proactive feedback of problems within the site. For example, sites often expand or change, and the names of pages sometimes change, but you might forget to change a link to the changed page. If a user clicks a link on your site and that page isn’t found, then sending an e-mail to the site administrator is very useful — the page in error can be quickly cor- rected so that no other users see the problem. Debugging and Tracing Controlling how errors are shown to the user is only part of the story when developing web sites, and tracking down those errors is just as important. Ideally, errors should be found during development and testing, and in many ways the job of testing is just as development. Many companies, Microsoft included, have teams of testers running through development projects, shaking out those errors. As a developer, you are bound to make mistakes, ranging from simple typing errors to more complex coding problems. The typing problems are usually easy to track down because they often cause compila- tion errors, but runtime errors can be more problematic. Tracing and debugging are the two main tech- niques used to find errors. 583 Dealing with Errors 18_042583 ch15.qxd 4/4/06 2:52 PM Page 583 Using ASP.NET Tracing You first looked at tracing in Chapter 14. It is the technique of adding code to your pages, for a few rea- sons: for debugging purposes, to output values of variables, or simply to find out where in your code certain things happen. The great thing about ASP.NET tracing is that not only is it extremely simple to do, but it’s also easily configurable and doesn’t require tracing code to be removed if you don’t want the trace information shown. What’s also great is that you get a wealth of additional information about the page, which can be useful for both debugging purposes and for learning about ASP.NET. Tracing Individual Pages Tracing can be turned on for individual pages by adding the Trace attribute to the Page directive: <%@ Page Trace=”true” %> On its own, this outputs a great deal of information about the page, but you can also add your own out- put using the Trace class, which has methods to write output to the trace log: Trace.Write(“my information”) The following Try It Out shows tracing in action. Try It Out Page-Level Tracing 1. In the Wrox United application for the chapter, open Checkout.aspx in Source View. 2. Add the Trace attribute to the Page directive: <%@ Page Trace=”True” %> 3. Save the file and run the application. 4. Add some items from the shop to your shopping cart and navigate to the Checkout page, where you’ll see that the bottom of the page has lots of information added. You might have to scroll down the page to see all of the information. 5. Switch back to VWD and open the code file for the Checkout page. 6. At the top of the Page_Load event, add the following line of code: Trace.Write(“In Page_Load”); 7. Before the check to see if the user is authenticated, add the following: Trace.Write(“In page_Load”, User.Identity.IsAuthenticated.ToString()); if (User.Identity.IsAuthenticated) 8. Save the page and run the application, again navigating to the Checkout page. 9. Scroll the page down so you can see the Trace Information section, as shown in Figure 15-6. 10. Here you can see that the output from the Trace.Write statements is mixed with the output that ASP.NET puts into the trace. Take a look at how this works and what information the trace output produces. 584 Chapter 15 18_042583 ch15.qxd 4/4/06 2:52 PM Page 584 Figure 15-6 11. Edit Checkout.aspx again and set the Trace attribute to False: <%@ Page Trace=”False” %> 12. Save the page and run the application, again navigating to the Checkout page. Notice that the trace information is gone from the page, even though the Trace.Write statements are still in the code. How It Works The first thing to look at is what all of this trace information is, and what it is useful for. There are many sections, as detailed in the following table. Section Contains Request Details Details of the request, such as the status code. Trace Information The flow of page events, showing the category, message, and time from the first to last byte of information sent to the browser. Control Tree The hierarchy of controls in the page. Session State Any session variables in use. Application State Any application variables in use. Request Cookies Collection The cookies stored for the current site. Response Cookies Collection Any cookies set during the page processing. Headers Collection The HTTP headers. Response Headers Collection Any headers set during the page processing. Form Collection Contents of the form. QueryString Collection Any querystring parameters for the request. Server Variables The HTTP server variables. 585 Dealing with Errors 18_042583 ch15.qxd 4/4/06 2:52 PM Page 585 All of this information is useful, although some sections are more useful than others. The Control Tree, for example, clearly shows the hierarchy of controls. You saw this in Chapter 14 when you looked at per- formance, but it’s also useful for understanding how the page is made up from the hierarchy of controls. At the top is the Page object, beneath that the Master page, and then controls within the Master page. This continues with all of the page objects, and shows the unique name of the control as well as its type. The Trace Information section shows the events in the order in which they are raised, so it is great for seeing exactly when things happen. Without any trace information of your own, the standard page events are shown, and anything you write is slotted into its correct space. So take a look what you actually did: protected void Page_Load(object sender, System.EventArgs e) { Trace.Write(“In Page_Load”); if (!Page.IsPostBack) { if (Profile.Cart == null) { NoCartlabel.Visible = true; Wizard1.Visible = false; } Trace.Write(“In Page_Load”, User.Identity.IsAuthenticated.ToString()); if (User.Identity.IsAuthenticated) Wizard1.ActiveStepIndex = 1; else Wizard1.ActiveStepIndex = 0; } In the first statement, you used Trace.Write to output a single string, which is displayed in the Message column. With the second Trace.Write, you passed in two parameters, and in this case, the first becomes the Category and the second becomes the Message. You can put trace statements anywhere within your code, and the output will be displayed in the Trace Information section, so it’s a great way to simply see what’s happening in your code. There is a also a Warn method of the Trace class, which outputs in the same way as Write, but the content is in red. This is useful for picking out statements within the trace output. The other thing you may have noticed is that by changing the value of the Trace attribute at the top of page to False, no trace output is displayed. You didn’t have to remove the Trace.Write statements from the code, because these are simply ignored if tracing isn’t enabled. This is great during develop- ment, because you can liberally sprinkle Trace.Write statements throughout your code to give you a good understanding of the program flow, and you can turn on or off the tracing without having to remove or comment out these statements. Tracing All Pages Although tracing in individual pages is useful, what’s great is being able to control tracing for the entire application. This is done with a configuration setting in Web.config, within the <system.web> section: <trace enabled=”True” /> 586 Chapter 15 18_042583 ch15.qxd 4/4/06 2:52 PM Page 586 [...]... their details globally, with the global.asax file You saw that for both trapped and untrapped exceptions, the details can be centrally logged, ensuring that you always know of errors wherever they happen with the application 595 Chapter 15 ❑ Tracing and debugging, and how you can track down problems within code Tracing gives the capability to write the status of the running code, with the capability to... who uses the code If you have reason to change the location of the file, you only have to change it once in Web.config and not every time it is mentioned within your code 599 Chapter 16 This doesn’t just stop with remote file locations, but also with connection strings If you are using a local database to test your code, you will have to change the connection settings as well Your local database might... in Figure 15-14 Figure 15-14 23 24 592 Keep pressing F11 until you are back into the checkout code Right-click the trans.Commit() line, and select the Run To Cursor option Notice how all intermediate code is run, but that the next line is the trans.Commit() line Dealing with Errors 25 26 From the Debug menu, select Delete All Breakpoints, or select Control+Shift+F9 Press F5 to continue the code, and... an exception so the debugger halts This is because of a change to the checkout code you did earlier, where you had an incorrect SQL statement so that you could force an exception 5 89 Chapter 15 Figure 15 -9 Figure 15-10 9 Click the View Detail link to show the details of the exception Click the plus sign (+) to expand the details (see Figure 15-11), and you’ll see that the exception shows your custom... time you step 19 Hover the cursor over the item of item.Quantity, and you’ll see the tooltip showing the value of the variable 20 Without moving the currently active line, hover the cursor over the item of the foreach line You’ll see another tooltip, but this time there isn’t a value That’s because this is a complex type, a CartItem, but notice that there is a little plus sign on the left 591 Chapter 15... not to copy, with settings like /R to overwrite existing files that are marked as read-only Common Problems Encountered When Deploying a Site You shouldn’t have any problems with the physical act of deployment itself However, what happens if you correctly copy all of the files over, install all of the relevant components, and install the third-party ones, and deployment still doesn’t work? With a fairly... NETWORK SERVICE for the App_Data folder The reason you have to do this is ASP.NET 2.0 runs under this particular service, and so if ASP.NET 2.0 wants to access the database, a request to access it will come from this particular service In ASP.NET 1.1, you would add permissions for the ASPNET account to do this In ASP.NET 2.0, the NETWORK SERVICE account does the same However, there have been occasions... unruly, so be warned It will turn up in the root folder of your application folder In Figure 16-11, you see the version of it that comes with Visual Web Developer by default 608 Deployment, Builds, and Finishing Up Problems with IIS Of course, if you test your site with ASP.NET Development Server, you might encounter some problems when you deploy your site on IIS First, make sure your application is... fix them In other ways, maintenance will be about performance monitoring: how fast is your site running? How many users is it handling? Can it cope with that? This is a science in itself This is one area where ASP.NET 2.0 has come on in leaps and bounds, with the addition of instrumentation, and the capability to instrument your application Instrumentation and Health Monitoring Instrumentation or instrumenting... posting back to the server Additionally, this chapter discussed the following topics: ❑ Exceptions, where you learned how to cope with the unexpected (cue the inquisition leaping in from off frame — ”Nobody expects the exception” — apologies to the Monty Python team) Dealing with exceptions is a tricky business, and should be limited to those situations where you can gracefully recover from the problem . the trans.Commit() line. 5 92 Chapter 15 18 _0 425 83 ch15.qxd 4/4 /06 2: 52 PM Page 5 92 25. From the Debug menu, select Delete All Breakpoints, or select Control+Shift+F9. 26 . Press F5 to continue. statement so that you could force an exception. 5 89 Dealing with Errors 18 _0 425 83 ch15.qxd 4/4 /06 2: 52 PM Page 5 89 Figure 15 -9 Figure 15- 10 9. Click the View Detail link to show the details of. but notice that there is a little plus sign on the left. 591 Dealing with Errors 18 _0 425 83 ch15.qxd 4/4 /06 2: 52 PM Page 591 Figure 15- 12 21. Hover over or click the +, and the properties (both