本文实现对MVC异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法。
第一步: 新建一个MVC应用程序, 2》从log4net官网下载log4net的文件包,然后在包的bin文件夹下的net文件夹中的4.0文件夹中找到log4net.dll文件,然后将它引入到我们的项目中来 3》在项目中添加一个名字为log4net.config的配置文件
log4net.config配置文件具体如下(这里仅仅是配置将错误信息写入到一个文本文件中)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender,log4net">
<param name="File" value="Log\\LogInfo\\" /><!--将日记文件记录到项目文件夹下的Log文件夹下的LogInfo文件夹中-->
<param name="AppendToFile" value="true" />
<param name="MaxSizeRollBackups" value="100" />
<param name="MaximumFileSize" value="10240KB" />
<param name="StaticLogFileName" value="false" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyy-MM-dd.TXT" /><!--日记文件名字就是当前日期,然后以TXT为后缀的txt文件(注意yyyy-MM-dd.TXT的这个TXT是大写 )-->
<param name="CountDirection" value="-1" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value=" 【Time】:%d%n 【Level】:%p%n 【Class】:%c%n 【ThreadID】: %thread %n 【FileAddress】:%F %LRow%n 【LogContent】:%m%n 【LogDetailed】:%exception %n---------------------------------------------------------------------------------------------------------------%n" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="LogFileAppender"/><!---注意:这段很重要-->
</root>
</log4net>
</configuration>
第二步:在web.config配置文件中配置log4net.config文件的路径,以及是否作为一个单独文件存在
<appSettings>
<add key="Log4net" value="1"/><!--这设置如果为1表示log4net.config文件是作为一个单独文件存在的,并没有配置在web.config中-->
<add key ="log4netPath" value="~/log4net.config"/> <!--设置log4net.config配置的文件的相对路径-->
</appSettings>
第三步:写一个自己的异常过滤器,让它继承HandleErrorAttribute类 然后重写OnException()这个方法 (注:HandleErrorAttribute 类已经继承了FilterAttribute, IExceptionFilter这两个接口,并实现了它们,所以这我们只要继承HandleErrorAttribute就可以了)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Itcast.CMS.WebApp.Controllers.Filters
{
public class MyExceptionFilterAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//创建一个log4net日记对象,参数传递一个出错类的类型
Itcast.CMS.Common.LogHelper log = new Common.LogHelper(filterContext.Controller.GetType());
//获取或设置一个值,该值指示是否已处理异常。如果已处理异常,则为 true;否则为 false。
if (filterContext.ExceptionHandled==true)
{
HttpException httpException = filterContext.Exception as HttpException;
//为什么要特别强调500 因为MVC处理HttpException的时候,如果为500 则会自动
//将其ExceptionHandled设置为true,那么我们就无法捕获异常
if (httpException.GetHttpCode() == 500)
{
return;
}
}
//创建一个HttpException对象,使用filterContext.Exception来对它初始化(其实就是将filterContext.Exception转换成HttpException类型)
HttpException httpExce = new HttpException(null, filterContext.Exception);
//HttpException httpExce = filterContext.Exception as HttpException;
if (httpExce != null)
{
if (httpExce.GetHttpCode() == 404)
{
HttpContext.Current.Response.Redirect("/Error/NotFound");//跳转到404错误页面
}
else if (httpExce.GetHttpCode() == 500)
{
//也可以这样写
filterContext.HttpContext.Response.Redirect("/Error/ServerError");//跳转到500服务器错误页面
}
}
//调用log4net来记录日记
log.Error("错误:" + filterContext.Exception.Message, filterContext.Exception);
//或者用自己的土办法写日记
Exception ex = filterContext.Exception; //获取抛出异常的对象
System.IO.File.AppendAllText(filterContext.HttpContext.Server.MapPath("/Logs/Log.txt"), ex.ToString()); //写日记
//告诉MVC框架,错误我已经处理了。你不需要在抛出黄页(即:不要再抛出异常页,也就不会跳转到错误页了)
filterContext.ExceptionHandled = true; //将异常设置为已处理
//如果想在出错的时候跳转到错误页的话,可以将它的值设为false
//filterContext.ExceptionHandled = false;
}
}
}
第三步:将自己刚刚写的过异常滤器注册到MVC的全局过滤器中去。只要到App_Start这个文件夹下的FilterConfig.cs文件中注册一下就可以,
将默认的 filters.Add(new HandleErrorAttribute()); 这段代码 替换为filters.Add(new MyExceptionFilterAttribute()); 具体代码如下
using Itcast.CMS.WebApp.Controllers.Filters;
using System.Web;
using System.Web.Mvc;
namespace Itcast.CMS.WebApp
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//将自定义的MyExceptionFilterAttribute过滤器注册为全局过滤器 (全局过滤器可以有多个)
filters.Add(new MyExceptionFilterAttribute());
}
}
}
做好以上两步,当MVC中有异常的时候,就会自动跳转到自己的过滤器中进行处理了。(注:不需要在类,或者方法前面加[MyExceptionController])只要做好前面两步就可以了
其实有的时候报404错误,也就是在路由阶段就没有通过,也就是没有到达控制器,所以无法进入到我们的过滤器。(去网上查了一下,都说404不是属于一个异常,仅仅是路径错误,这时候只要去Web.cofig配置文件中,配置一下,当出现404错误的时候,就跳转到我们自定义的错误页去)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<!--定义友好的错误,出错默认导向到 我们定义好的友好错误页面 -->
<!--mode属性:mode="On"的时候就表示对所以访问者都显示定制错误页;mode="Off"就表示关闭定制的错误页,页面错误的时候将本地和远程用户都会看到详细的错误信息;mode="RemoteOnly"就表示本地用户将看到详细错误信息(这里的本地的意思即:你直接登陆网站服务器上),而远程用户将会看到程序员定制的错误页。所以一般情况下我们都建议将mode属性设为RemoteOnly-->
<!--RedirectMode 属性:获取或设置一个值,该值指示在将用户重定向到自定义错误页面时,是否应更改请求的 URL。默认值为 ResponseRedirect。如果 RedirectMode 属性设置为 ResponseRedirect,则将用户重定向到该错误页面,并且原始 URL 更改为该错误页面的 URL。如果 RedirectMode 属性设置为 ResponseRewrite,则将用户定向到错误页面,并且不更改浏览器中的原始 URL。【ResponseRedirect其实就是Response.Redirect;而ResponseRewrite其实就是Server.Transfer】 -->
<!--defaultRedirect属性:应用程序在发生错误时重定向到的默认URL-->
<!--<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/Error/notfound">-->
<!--在它下面还可以分的很详细,可以针对一些错误码来指向我们针对这些错误码而定制的页面-->
<!--<error statusCode="403" redirect="/Error/403" />-->
<!--<error statusCode="404" redirect="/Error/404" />-->
<!--</customErrors>-->
<customErrors mode="On" >
<error statusCode="404" redirect="/Error/notfound"/><!--出现404错误的时候跳转到Error控制器下的notfound方法-->
</customErrors>
</system.web>
</configuration>
第四步:配置好上面这些就基本OK了。那现在我们就创建一个Home控制器来测试一下这个log4net的日记功能
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
int a = 0;
int b = 1;
int c = b / a; //这里会报错,因为0不能作为被除数。在这里抛异常,会跳到MyExceptionFilterAttribute这个异常过滤器中执行OnException()这段方法
return View();
}
}
}
OK。测试可以通过,有记录日记个功能。
附赠:logHelper.cs文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Itcast.CMS.Common
{
public class LogHelper
{
private log4net.ILog log = null;
//到时候在过滤器中new这个对象的时候,将出错的类的类型传递进来,这样就获取到了出错的类的类型了
public LogHelper(Type traceType)
{
//如果Web.config配置文件中的key为log4net的value值为1,就表示log4net的配置文件是作为一个单独文件进行配置的,
if (System.Configuration.ConfigurationManager.AppSettings["log4net"].ToString() == "1")
{
//那么我就获取log4net的配置文件,并将它加载到我们的项目中去(web.config配置文件的内容是:<add key ="log4netPath" value="~/log4net.config"/>)
string filePath = System.Configuration.ConfigurationManager.AppSettings["log4netPath"].ToString();
//要特别注意这里很有可能获取到的这个filePath不能用,所以在使用的时候特别要留意这里
//System.IO.FileInfo file = new System.IO.FileInfo(filePath);
//最好是将filePath这个相对路径转换成物理物理再使用
System.IO.FileInfo file = new System.IO.FileInfo(HttpContext.Current.Request.MapPath(filePath));
//Getlogger()静态方法,用来检索框架里是否存在logger对象,如果不存在就创建一个名字为logger的对象
log4net.Config.XmlConfigurator.Configure(file);
}
else
{
log4net.Config.XmlConfigurator.Configure();
}
//log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
log = log4net.LogManager.GetLogger(traceType);
}
/// <summary>
/// 程序运行的过程中的,一般信息可以调用此方法记录日记
/// </summary>
/// <param name="info"></param>
public void Info(string info)
{
if (log.IsInfoEnabled)
{
log.Info(info);
}
}
/// <summary>
/// 程序运行的过程中的,一般信息可以调用此方法记录日记
/// </summary>
/// <param name="info"></param>
/// <param name="ex"></param>
public void Info(string info, Exception ex)
{
if (log.IsInfoEnabled)
{
log.Info(info, ex);
}
}
/// <summary>
/// 程序出现错误的时候调用此方法记录日记(一般用在出现了异常以后)
/// </summary>
/// <param name="info"></param>
public void Error(string info)
{
if (log.IsErrorEnabled)
{
log.Error(info);
}
}
/// <summary>
/// 程序出现错误的时候调用此方法记录日记(一般用在出现了异常以后)
/// </summary>
/// <param name="info"></param>
/// <param name="ex"></param>
public void Error(string info, Exception ex)
{
if (log.IsErrorEnabled)
{
log.Error(info, ex);
}
}
/// <summary>
/// 程序员觉得任何有利于程序在调试时更详细的了解系统运行状态的信息,比如变量的值等等,都可以调用此方法记录到日记
/// </summary>
/// <param name="info"></param>
public void Debug(string info)
{
if (log.IsDebugEnabled)
{
log.Debug(info);
}
}
/// <summary>
/// 程序员觉得任何有利于程序在调试时更详细的了解系统运行状态的信息,比如变量的值等等,都可以调用此方法记录到日记
/// </summary>
/// <param name="info"></param>
/// <param name="ex"></param>
public void Debug(string info, Exception ex)
{
if (log.IsDebugEnabled)
{
log.Debug(info, ex);
}
}
/// <summary>
/// 程序出现警告时调用此方法记录日记(程序出现警告不会使程序出现异常,但是可能会影响程序性能)
/// </summary>
/// <param name="info"></param>
public void Warn(string info)
{
if (log.IsWarnEnabled)
{
log.Warn(info);
}
}
/// <summary>
/// 程序出现警告时调用此方法记录日记(程序出现警告不会使程序出现异常,但是可能会影响程序性能)
/// </summary>
/// <param name="info"></param>
/// <param name="ex"></param>
public void Warn(string info, Exception ex)
{
if (log.IsWarnEnabled)
{
log.Warn(info, ex);
}
}
/// <summary>
/// 程序出现特别严重的错误,一般是在应用程序崩溃的时候调用此方法记录日记
/// </summary>
/// <param name="info"></param>
public void Fatal(string info)
{
if (log.IsFatalEnabled)
{
log.Fatal(info);
}
}
/// <summary>
/// 程序出现特别严重的错误,一般是在应用程序崩溃的时候调用此方法记录日记
/// </summary>
/// <param name="info"></param>
/// <param name="ex"></param>
public void Fatal(string info, Exception ex)
{
if (log.IsFatalEnabled)
{
log.Fatal(info, ex);
}
}
}
}
参考别人的代码: