全局文件Global.asax
添加Web→全局应用程序类,注意文件名不要改。
全局文件是对Web应用生命周期的一个事件响应的地方
将Web应用启动时初始化的一些代码写到Application_Start中。应用关闭的时候Application_End调用。
当一个Session启动的时候Session_Start被调用,Session结束(用户主动退出或者超时结束)Session_End被调用。
当一个用户请求来的时候Application_BeginRequest方法被调用
当应用中出现未捕获异常,Application_Error被调用(常考,ASP.Net中的错误处理机制),用HttpContext.Current.Server.GetLastError()获得异常信息,然后用Log4Net记录到日志中。
案例练习1:屏蔽指定的IP地址(Application_BeginRequest)。
案例练习2:实现简单url重写。无Cookie实现Session、伪静态Seo。(Application_BeginRequest,在第8个事件之前都可以做url重写,因为在第7个事件之后才会使用url)
基本思路:
1.通过正则分析url。
2.HttpContext.Current.RewritePath("~/ContactsList.aspx");//不是Response.Redirect();
另一种方式
HttpContext.Current.RemapHandler(new 页面对象());//如果在Application_BeginRequest事件中已经这么做了,那么意味着在BeginRequest中已经创建了页面对象,那么在HttpApplication的第7个事件后就不再创建页面对象了。
图片防盗链发过去一个错误图片同样浪费资源。
1 protected void Application_BeginRequest(object sender, EventArgs e) 2 { 3 var Request = HttpContext.Current.Request; 4 if (Request.Url.PathAndQuery.StartsWith("/domDemo2/images/mm/"))//防止美女时钟的图片盗链 5 { 6 //判断是否盗链 7 if (Request.UrlReferrer == null || !IsSameHost(Request.UrlReferrer, Request.Url)) 8 { 9 HttpContext.Current.Response.Write("请勿直接访问图片,请在美女时钟页面中访问!"); 10 HttpContext.Current.Response.End(); 11 } 12 } 13 } 14 15 /// <summary> 16 /// 判断uri1和uri2是否是在同一台主机上 17 /// </summary> 18 /// <param name="uri1"></param> 19 /// <param name="uri2"></param> 20 /// <returns></returns> 21 private static bool IsSameHost(Uri uri1, Uri uri2) 22 { 23 return Uri.Compare(uri1, uri2, UriComponents.Host, UriFormat.SafeUnescaped, StringComparison.CurrentCultureIgnoreCase) == 0; 24 }
错误页
当页面发生错误的时候,ASP.Net会将错误信息展示出来(Sqlconnection的错误就能暴露连接字符串),这样一来不好看,二来会泄露网站的内部实现信息,给网站带来安全隐患,因此需要定制错误页,发生错误时显示开发人员定制的页面。404页面放点广告也是好的嘛。
配置web.config,配置customErrors区域:
1 <customErrors mode="On" defaultRedirect="MyErrorPage.aspx"> 2 <error statusCode="403" redirect="NoAccess.htm" /> 3 <error statusCode="404" redirect="FileNotFound.htm" /> 4 </customErrors>
mode三个可选值:On:总是显示定制错误页面;Off:不显示定制错误界面,直接显示调用堆栈等异常信息;remoteonly:对于本机的访问显示调用堆栈等异常信息,对于外部用户的显示定制错误页面。一般设置为RemoteOnly,这样发生错误的话,管理员可以在服务器的浏览器中看详细错误信息,普通用户看不到。学习演示的时候mode设置为On,否则看不到定制页。
全局错误处理
-》在全局应用程序类中为事件Application_Error添加处理代码
-》当出错时,应该做什么呢?
事务一:记录
事务二:转到友好提示页面
-》实现记录
定义帮助类,完成持久化存储
问题:如果同时发生多个错误,则写操作都去抢占文件会记录错误信息
解决:加锁
新问题:用户等待时间太长
新解决:将错误写到队列中,再开新线程将队列中的信息写到文件中
-》最终实现:定义日志帮助类,两个方法
方法一:用于向队列中写信息,出错时调用
方法二:用于将队列中的信息写到文件中,在静态构造方法中调用
-》说明:将日志文件生成到App_Code文件夹下,这样浏览者就无法通过浏览器访问了
-》问题的关键
《1》为什么要加锁:每个浏览器的请求,都开启一个线程进行处理
《2》为什么要向内存中写:向文件中写加锁后用户会等待很长时间
-》通过配置文件转换错误提示页
在web.config文件中的system.web节点添加如下错误处理配置:
<customErrors mode="On" defaultRedirect="error/error.htm">
<error statusCode="404" redirect="error/404.htm" />
</customErrors>
-》模式共有3个参数
On表示启用自定义错误跳转
Off表示禁用自定义错误跳转
RemoteOnly表示仅远程访问时出错跳转
案例:在Application_Error中进行页面跳转,记录日志。
Response.Redirect("~/Error.htm");或者在Web.config中配置。
Server.GetLastError()获取错误异常。
将日志记录到log.txt(将该文件放到其他磁盘或者App_Data中)文件中。问题:如果直接写log.txt文件,那么请求人数多的时候会有问题,并发访问问题,如果加lock()又会严重影响性能。解决办法:使用队列。
Index.aspx
1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="t4_ErrorHandler.Index" %> 2 3 <!DOCTYPE html> 4 5 <html xmlns="http://www.w3.org/1999/xhtml"> 6 <head runat="server"> 7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 8 <title></title> 9 </head> 10 <body> 11 <form id="form1" runat="server"> 12 <div> 13 14 <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /> 15 16 </div> 17 </form> 18 </body> 19 </html>
Index.aspx.cs
1 public partial class Index : System.Web.UI.Page 2 { 3 protected void Page_Load(object sender, EventArgs e) 4 { 5 6 } 7 8 protected void Button1_Click(object sender, EventArgs e) 9 { 10 throw new Exception("小笼包"); 11 } 12 }
Global.asax
1 public class Global : System.Web.HttpApplication 2 { 3 4 protected void Application_Start(object sender, EventArgs e) 5 { 6 //这里的代码只被调用一次,所以只会开启一个写文件的线程 7 LogHelper.WriteFile(); 8 } 9 10 protected void Session_Start(object sender, EventArgs e) 11 { 12 13 } 14 15 protected void Application_BeginRequest(object sender, EventArgs e) 16 { 17 18 } 19 20 protected void Application_AuthenticateRequest(object sender, EventArgs e) 21 { 22 23 } 24 25 //只要在运行过程中发生错误,就会执行Error事件的处理函数 26 protected void Application_Error(object sender, EventArgs e) 27 { 28 ////记录错误信息 29 LogHelper.WriteLog(); 30 31 //转到友好提示页面 32 Response.Redirect("/Error/ShowError.html"); 33 } 34 35 protected void Session_End(object sender, EventArgs e) 36 { 37 38 } 39 40 protected void Application_End(object sender, EventArgs e) 41 { 42 43 } 44 }
ShowError.html
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 <h1>出错了</h1> 9 </body> 10 </html>
LogHelper.cs
1 public class LogHelper 2 { 3 private static Queue<string> errorQueue; 4 5 static LogHelper() 6 { 7 errorQueue = new Queue<string>(); 8 } 9 10 public static void WriteFile() 11 { 12 //将一个方法交给线程执行:使用lambda构造委托对象 13 Thread thread = new Thread(() => 14 { 15 while (true) 16 { 17 if (errorQueue.Count > 0) 18 { 19 20 string errorString = errorQueue.Dequeue(); 21 22 string logFilePath = errorString.Split(‘$‘)[0]; 23 string errorString2 = errorString.Split(‘$‘)[1]; 24 25 File.AppendAllText(logFilePath, errorString2); 26 } 27 else 28 { 29 Thread.Sleep(5000); 30 } 31 } 32 }); 33 thread.IsBackground = true; 34 thread.Start(); 35 36 } 37 38 public static void WriteLog() 39 { 40 string errorStack = HttpContext.Current.Error.InnerException.StackTrace + "\r\n------------\r\n"; 41 42 string fileName = DateTime.Now.ToString("yyyy-MM-dd"); 43 44 string logFilePath = HttpContext.Current.Request.MapPath("/app_code/" + fileName + ".txt"); 45 46 //每个浏览器请求过来,都会开启一个新线程执行管道事件 47 //防止多线程写文件时的错误,而进行一个加锁操作 48 49 //这里虽然使用了内存中的对象,但是并没有解决多线程问题,所以要继续加锁 50 lock ("dlb") 51 { 52 errorQueue.Enqueue(logFilePath + "$" + errorStack); 53 } 54 } 55 }
原文:http://www.cnblogs.com/ninghongkun/p/6368162.html