MVC过滤器

  • 一般的过滤器执行顺序
  1. IAuthorizationFilter->OnAuthorization(授权)
  2. IActionFilter          ->OnActionExecuting(行为)
  3. Action
  4. IActionFilter          ->OnActionExecuted(行为)
  5. IResultFilter          ->OnResultExecuting(结果)
  6. View
  7. IResultFilter          ->OnResultExecuted(结果)
  8. *IExceptionFilter    ->OnException(异常),此方法并不在以上的顺序执行中,有异常发生时即会执行,有点类似于中断
  • 当同时在Controller和Action中都设置了过滤器后,执行顺序一般是由外到里,即“全局”->“控制器”->“行为”
  1. Controller->IAuthorizationFilter->OnAuthorization
  2. Action     ->IAuthorizationFilter->OnAuthorization
  3. Controller->IActionFilter          ->OnActionExecuting
  4. Action     ->IActionFilter          ->OnActionExecuting
  5. Action
  6. Action     ->IActionFilter          ->OnActionExecuted
  7. Controller->IActionFilter          ->OnActionExecuted
  8. Controller->IResultFilter          ->OnResultExecuting
  9. Action     ->IResultFilter          ->OnActionExecuting
  10. Action     ->IResultFilter          ->OnActionExecuted
  11. Controller->IResultFilter          ->OnActionExecuted
  • 因为异常是从里往外抛,因次异常的处理顺序则刚好相反,一般是由里到外,即“行为”->“控制器”->“全局”
  1. Action     ->IExceptionFilter->OnException
  2. Controller->IExceptionFilter->OnException

系统自带的异常处理
我们习惯使用的过滤器,要么是为Action加上Attribute,要么就是为Controller加上Attribute。上面所说的全局过滤器是怎么回事呢?先看看Gloabal里的代码:
protected void Application_Start(){ 
//注册Area AreaRegistration.RegisterAllAreas(); 
//注册过滤器 RegisterGlobalFilters(GlobalFilters.Filters); 
//注册路由 RegisterRoutes(RouteTable.Routes);
}
 public static void RegisterGlobalFilters(GlobalFilterCollection filters)

    filters.Add(new HandleErrorAttribute()); 
}

由上可知,在应用程序启动的时候就已经注册了全局过滤器,HandleErrorAttribute就是系统自带的异常过滤器。在这注册的全局过滤器,可以不用到每个Controller或者是每个Action去声明,直接作用于全局了,即可以捕捉整个站点的所有异常。看看它的源码是怎么处理异常的:

public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
{
Exception innerException = filterContext.Exception;
if ((new HttpException(null, innerException).GetHttpCode() == ) && this.ExceptionType.IsInstanceOfType(innerException))
{
string controllerName = (string) filterContext.RouteData.Values["controller"];
string actionName = (string) filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
ViewResult result = new ViewResult {
ViewName = this.View,
MasterName = this.Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.Result = result;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = ;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
}

HandleErrorAttribute的异常处理逻辑里,生成了一个HandleErrorInfo类的Model,并设置返回的结果为一个新生成的ViewResult。这个视图默认的ViewName是Error,对应于Share文件夹里的Error视图。而自带的Error视图没有用到HandleErrorInfo的Model,因此公开的信息也不是很多,可以根据具体的需求改造一下。例如:

@model HandleErrorInfo
<br />
<div class="container">
<div class="alert alert-error">
<h4> Exception:</h4> <br />
<p>
There was a <b>@Model.Exception.GetType().Name</b> while rendering <b>@Model.ControllerName</b>'s<b>@Model.ActionName</b> action.</p> <p> @Model.Exception.Message
</p>
</div>
<div class="alert"> <h4> Stack trace:</h4> <br /> <pre>@Model.Exception.StackTrace</pre> </div>
</div>

这个过滤器要能起效,还需要在配置文件中配置一下:<customErrors mode="On" />

自定义的异常统一处理

在实现异常的统一处理之前,先来明确一下需求:

  1. 站点所有页面在异常发生后,均需要记录异常日志,并转向错误提示页面(异常内容的详略程度由具体需求决定)
  2. 所有返回JSON数据的异步请求,不但需要记录异常日志,而且需要向客户端返回JSON格式的错误信息提示,而不是转向错误提示页面(异步请求也不可能转向错误提示页面)
  3. 采用AOP思想,将异常处理解耦
  4. 尽量精简声明Attribute的重复代码
  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class JsonExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
//返回异常JSON
filterContext.Result = new JsonResult
{
Data = new { Success = false, Message = filterContext.Exception.Message }
};
}
}
}
说明:需要注意的是,不需要调用base.OnException,否则会跳过LogExceptionAttribute先执行HandleErrorAttribute的处理逻辑,从而返回结果不再是JsonResult,而是ViewResult,客户端也就无法处理非JSON的结果了。
这里也不需要设置filterContext.ExceptionHandled = true,否则在LogExceptionAttribute处理时,因为 !filterContext.ExceptionHandled 的判断条件,LogExceptionAttribute的逻辑不会执行,也就不会记录异常日志了。 
   [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class LogExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
string msgTemplate = "在执行 controller[{0}] 的 action[{1}] 时产生异常";
LogManager.GetLogger("LogExceptionAttribute").Error(string.Format(msgTemplate, controllerName, actionName), filterContext.Exception);
} if (filterContext.Result is JsonResult)
{
//当结果为json时,设置异常已处理
filterContext.ExceptionHandled = true;
}
else
{
//否则调用原始设置
base.OnException(filterContext);
}
}
}

修改全局过滤器:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleError2LogAttribute());//全局的日志异常过滤器
//filters.Add(new HandleErrorAttribute());
}
调用示例:

[HttpPost]
[JsonException]
public JsonResult Add(string ip, int port)
{
... //处理逻辑
return Json(new { Success = true, Message = "添加成功" });
}

MVC异常过滤器的更多相关文章

  1. MVC异常过滤器 (错误页)

    控制器 using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syste ...

  2. ASP.NET Core 中间件 自定义全局异常中间件以及 MVC异常过滤器作用

    中间件是一种装配到应用管道以处理请求和响应的软件. 每个组件: 选择是否将请求传递到管道中的下一个组件. 可在管道中的下一个组件前后执行工作. 请求委托用于生成请求管道. 请求委托处理每个 HTTP ...

  3. MVC异常过滤器在三种作用范围下的执行顺序

    对于一般过滤器(即:除了IExceptionFilter ),当同时在Controller和Action中都设置了同一个过滤器后(例如IActionFilter),执行顺序一般是由外到里,即“全局”- ...

  4. MVC与WebApi中的异常过滤器

    一.MVC的异常过滤器   1.自定义MVC异常过滤器 创建一个类,继承HandleErrorAttribute即可,如果不需要作为特性使用直接实现IExceptionFilter接口即可, 注意,该 ...

  5. MVC异常日志生产者消费者模式记录(异常过滤器)

    生产者消费者模式 定义自己的异常过滤器并注册 namespace Eco.Web.App.Models { public class MyExceptionAttribute : HandleErro ...

  6. MVC 全局异常过滤器HandleErrorAttribute

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  7. asp.net core MVC 全局过滤器之ExceptionFilter异常过滤器(一)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter异常过滤器(一) asp.net cor ...

  8. 笨鸟先飞之ASP.NET MVC系列之过滤器(06异常过滤器)

    概念介绍 异常过滤器主要在我们方法中出现异常的时候触发,一般我们用 异常过滤器 记录日志,或者在产生异常时做友好的处理 如果我们需要创建异常过滤器需要实现IExceptionFilter接口. nam ...

  9. MVC教程九:异常过滤器

    我们平常在程序里面为了捕获异常,会加上try-catch-finally代码,但是这样会使得程序代码看起来很庞大,在MVC中我们可以使用异常过滤器来捕获程序中的异常,如下图所示: 使用了异常过滤器以后 ...

随机推荐

  1. 管理ONS(Oracle Notification Service)

    Onsctl Onsctl这个命令是用来管理ONS(Oracle Notification Service)是OracleClustser实现FAN Event Push模型的基础. Oracle N ...

  2. 《鸟哥的Linux私房菜-基础学习篇(第三版)》(三)

    第2章 Linxu怎样学习         1. Linux当前的应用角色 当前的Linux常见的应用可略分为企业应用和个人应用双方面. 首先谈了企业环境的利用. 1)网络server. 2)关键任务 ...

  3. FZOJ--2212--Super Mobile Charger(水题)

    Problem 2212 Super Mobile Charger Accept: 3    Submit: 11 Time Limit: 1000 mSec    Memory Limit : 32 ...

  4. IIS 7.5 配置

    安装 MVC 3 对应版本. 或者对应的版本. 登陆时候报错 检查下面几项是否配置正确 检查ASP.net 是否开启. 检查网站的应用池是否配置正确 在处理程序映射当中检查这几项是否配置正确 如果都配 ...

  5. 显示gif动画(帧动画的播放)

    在android上显示gif不太方便,虽然有控件可以实现,但是效果不是很好,保险点儿的作法还是使用帧动画来处理.①在XML中定义animation-list:<?xml version=&quo ...

  6. [ SQLServer ] 數字類型的欄位細節 - 轉載

    [MSSQL] 欄位開立(2) - decimal, numeric, float, real, money 的抉擇 https://dotblogs.com.tw/henryli/2015/06/1 ...

  7. cuda thrust函数首次调用耗费时间比后续调用长原因

    lazy context initialisation. stackoverflow

  8. HDU 1166 敌兵布阵(线段树单节点更新 区间求和)

    http://acm.hdu.edu.cn/showproblem.php?pid=1166 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Dere ...

  9. BZOJ 3637: Query on a tree VI LCT_维护子树信息_点权转边权_好题

    非常喜欢这道题. 点权转边权,这样每次在切断一个点的所有儿子的时候只断掉一条边即可. Code: #include <cstring> #include <cstdio> #i ...

  10. NodeJS学习笔记 (19)进阶调试-debugger(ok)

    写在前面 谈到node断点调试,目前主要有三种方式,通过node内置调试工具.通过IDE(如vscode).通过node-inspector,三者本质上差不多.本文着重点在于介绍 如何在本地通过nod ...