详解ASP.NET MVC 常用扩展点:过滤器、模型绑定

时间:2021-05-25

一、过滤器(Filter)

ASP.NET MVC中的每一个请求,都会分配给对应Controller(以下简称“控制器”)下的特定Action(以下简称“方法”)处理,正常情况下直接在方法里写代码就可以了,但是如果想在方法执行之前或者之后处理一些逻辑,这里就需要用到过滤器。

常用的过滤器有三个:Authorize(授权过滤器),HandleError(异常过滤器),ActionFilter(自定义过滤器),对应的类分别是:AuthorizeAttribute、HandleErrorAttribute和ActionFilterAttribute,继承这些类并重写其中方法即可实现不同的功能。

1.Authorize授权过滤器

授权过滤器顾名思义就是授权用的,授权过滤器在方法执行之前执行,用于限制请求能不能进入这个方法,新建一个方法:

public JsonResult AuthorizeFilterTest(){ return Json(new ReturnModel_Common { msg = "hello world!" });}

直接访问得到结果:

现在假设这个AuthorizeFilterTest方法是一个后台方法,用户必须得有一个有效的令牌(token)才能访问,常规做法是在AuthorizeFilterTest方法里接收并验证token,但是这样一旦方法多了,每个方法里都写验证的代码显然不切实际,这个时候就要用到授权过滤器:

public class TokenValidateAttribute : AuthorizeAttribute { /// <summary> /// 授权验证的逻辑处理。返回true则通过授权,false则相反 /// </summary> /// <param name="httpContext"></param> /// <returns></returns> protected override bool AuthorizeCore(HttpContextBase httpContext) { string token = httpContext.Request["token"]; if (string.IsNullOrEmpty(token)) { return false; } else { return true; } } }

新建了一个继承AuthorizeAttribute的类,并重写了其中的AuthorizeCore方法,这段伪代码实现的就是token有值即返回true,没有则返回false,标注到需要授权才可以访问的方法上面:

[TokenValidate]public JsonResult AuthorizeFilterTest(){ return Json(new ReturnModel_Common { msg = "hello world!" })}

标注TokenValidate后,AuthorizeCore方法就在AuthorizeFilterTest之前执行,如果AuthorizeCore返回true,那么授权成功执行AuthorizeFilterTest里面的代码,否则授权失败。不传token:

传token:

不传token授权失败时进入了MVC默认的未授权页面。这里做下改进:不管授权是成功还是失败都保证返回值格式一致,方便前端处理,这个时候重写AuthorizeAttribute类里的HandleUnauthorizedRequest方法即可:

/// <summary>/// 授权失败处理/// </summary>/// <param name="filterContext"></param>protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext){ base.HandleUnauthorizedRequest(filterContext); var json = new JsonResult(); json.Data = new ReturnModel_Common { success = false, code = ReturnCode_Interface.Token过期或错误, msg = "token expired or error" }; json.JsonRequestBehavior = JsonRequestBehavior.AllowGet; filterContext.Result = json;}

效果:

实战:授权过滤器最广泛的应用还是做权限管理系统,用户登录成功后服务端输出一个加密的token,后续的请求都会带上这个token,服务端在AuthorizeCore方法里解开token拿到用户ID,根据用户ID去数据库里查是否有请求当前接口的权限,有就返回true,反之返回false。这种方式做授权,相比登录成功给Cookie和Session的好处就是一个接口PC端、App端共同使用。

2.HandleError异常过滤器

异常过滤器是处理代码异常的,在系统的代码抛错的时候执行,MVC默认已经实现了异常过滤器,并且注册到了App_Start目录下的FilterConfig.cs:

filters.Add(new HandleErrorAttribute());

这个生效于整个系统,任何接口或者页面报错都会执行MVC默认的异常处理,并返回一个默认的报错页面:Views/Shared/Error(程序发到服务器上报错时才可以看到本页面,本地调试权限高,还是可以看到具体报错信息的)

@{ Layout = null;}<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width" /> <title>错误</title></head><body> <hgroup> <h1>错误。</h1> <h2>处理你的请求时出错。</h2> </hgroup></body></html>

默认的异常过滤器显然无法满足使用需求,重写下异常过滤器,应付项目实战中的需求:

1)报错可以记录错误代码所在的控制器和方法,以及报错时的请求参数和时间;

2)返回特定格式的JSON方便前端处理。因为现在系统大部分是ajax请求,报错了返回MVC默认的报错页面,前端不好处理

新建一个类LogExceptionAttribute继承HandleErrorAttribute,并重写内部的OnException方法:

public override void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; string param = Common.GetPostParas(); string ip = HttpContext.Current.Request.UserHostAddress; LogManager.GetLogger("LogExceptionAttribute").Error("Location:{0}/{1} Param:{2}UserIP:{3} Exception:{4}", controllerName, actionName, param, ip, filterContext.Exception.Message); filterContext.Result = new JsonResult { Data = new ReturnModel_Common { success = false, code = ReturnCode_Interface.服务端抛错, msg = filterContext.Exception.Message }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } if (filterContext.Result is JsonResult) filterContext.ExceptionHandled = true;//返回结果是JsonResult,则设置异常已处理 else base.OnException(filterContext);//执行基类HandleErrorAttribute的逻辑,转向错误页面 }

异常过滤器就不像授权过滤器一样标注在方法上面了,直接到App_Start目录下的FilterConfig.cs注册下,这样所有的接口都可以生效了:

filters.Add(new LogExceptionAttribute());

异常过滤器里使用了NLog作为日志记录工具,Nuget安装命令:

Install-Package NLogInstall-Package NLog.Config

相比Log4net,NLog配置简单,仅几行代码即可,NLog.config:

<?xml version="1.0" encoding="utf-8" ?><nlog xmlns="http://mon { msg = "Name:" + student.Name + ",长度:" + student.Name.Length + " Class:" + student.Class + ",长度:" + student.Class.Length }); }}

可见,标注了Trim属性的Name长度是去除空格的长度:7,而没有标注的Class属性的长度则是6。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章