宝哥软件园

详细说明ASP中MVC的常见扩展点 NET:筛选器和模型绑定

编辑:宝哥软件园 来源:互联网 时间:2021-09-05

一、过滤器(过滤)

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

有三种常用的过滤器:授权过滤器、处理错误过滤器和操作过滤器。对应的类是AuthorizeAttribute、HandleErrorAttribute和ActionFilterAttribute。继承这些类并重写其中的方法可以实现不同的功能。

1.授权授权过滤器

授权过滤器,顾名思义,用于授权。授权过滤器在方法执行之前执行,用于限制请求是否可以进入该方法。创建新方法:

public Json result AuthorizeFilterTest(){ return Json(new return model _ Common { msg=' hello world!'});}直接访问结果:

现在假设这个AuthorizeFilterTest方法是一个后台方法,用户必须有一个有效的令牌才能访问它。常规的方法是在AuthorizeFilterTest方法中接收和验证令牌,但是一旦方法多了,在每个方法中写验证码显然是不切实际的,所以此时会用到授权过滤器:

公共类token valid attribute : authorization attribute {///summary////授权身份验证的逻辑处理。返回true以传递授权,否则返回True////summary///param name=' httpcontext '/param///returns/returns protected override bool authorize core(httpcontext false httpcontext){ string token=httpcontext。请求['令牌'];if(字符串。IsNullOrEmpty(token)){ return false;} else { return true}}}创建了一个继承AuthorizeAttribute的新类,并在其中重写了AuthorizeCore方法。这个伪代码意识到,如果token有值,它将返回true,如果没有值,它将返回false,并在需要授权才能访问的方法上标记它:

[token validate]public JsonResult AuthorizeFilterTest(){ return Json(new return model _ Common { msg=' hello world!'})}标记TokenValidate后,在AuthorizeFilterTest之前执行AuthorizeCore方法。如果AuthorizeCore返回true,则AuthorizeFilterTest中的代码被授权成功执行,否则授权失败。不传递令牌:

传递令牌:

未通过令牌授权失败时,进入MVC默认未授权页面。在这里做改进:无论授权成功还是失败,返回值格式都保证一致,方便前端处理。此时,在AuthorizeAttribute类中重写HandleUnauthorizedRequest方法:

///summary////授权失败处理////summary///param name=' filter context '/param protected override void handleunauthorized request(AuthorizationContext filter context){ base。handleunauthorized request(filterContext);var JSON=new JsonResult();JSON . data=new return model _ common { success=false,code=return code _ interface . token过期或错误,msg='token过期或错误' };json。JsonRequestBehavior=JsonRequestBehavior。AllowGetfilterContext。结果=json}效果:

实战:使用最广泛的授权过滤器是权限管理系统。用户成功登录后,服务器输出一个加密的令牌,后续的请求会带来这个令牌。服务器在AuthorizeCore方法中解锁令牌获取用户id,并根据用户ID检查数据库,看是否有权限请求当前接口。如果是,则返回true,否则返回false。与成功登录相比,这种授权方式的优势在于赋予了Cookie和Session,即PC和App一个界面共同使用。

2.处理错误异常过滤器

异常过滤器用于处理代码异常,并在系统代码抛出错误时执行。MVC已经默认实现了异常过滤器,并将其注册在App_Start目录下的FilterConfig.cs中:

过滤器添加(new handleerrortattribute());这个生效于整个系统,任何接口或者页面报错都会执行手动音量调节默认的异常处理,并返回一个默认的报错页面:视图/共享/错误(程序发到服务器上报错时才可以看到本页面,本地调试权限高,还是可以看到具体报错信息的)

@ { 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/headbody hgroup h1错误h1/H2处理你的请求时出错/h2 /hgroup/body/html默认的异常过滤器显然无法满足使用需求,重写下异常过滤器,应付项目实战中的需求:

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

2)返回特定格式的JSON方便前端处理。因为现在系统大部分是创建交互式、快速动态网页应用的网页开发技术请求,报错了返回手动音量调节默认的报错页面,前端不好处理

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

public override void one exception(异常上下文筛选器上下文){ if(!filterContext .异常已处理){ string controllerName=(字符串)筛选器上下文.路由数据。值['控制器'];字符串actionName=(字符串)filterContext .路由数据。值['操作'];字符串参数=公共getpost parates();字符串ip=HttpContext .当前请求。用户主机地址日志管理器GetLogger(' LogExceptionAttribute ').错误('位置:{0}/{1}参数:{ 2 }用户IP:{3}异常:{4} ',控制器名,操作名,参数,IP,筛选器上下文。异常。消息);filterContext .结果=新JsonResult {数据=新返回模型_公共{成功=假,代码=返回代码_接口.服务端抛错,msg=filterContext .异常。消息},JsonRequestBehavior=JsonRequestBehavior .允许获取};} if (filterContext .结果是JsonResult) filterContext .ExceptionHandled=true//返回结果是JsonResult,则设置异常已处理否则基地OnException(筛选器上下文);//执行基类HandleErrorAttribute的逻辑,转向错误页面}异常过滤器就不像授权过滤器一样标注在方法上面了,直接到应用程序_开始目录下的FilterConfig.cs注册下,这样所有的接口都可以生效了:

过滤器。添加(新的LogExceptionAttribute());异常过滤器里使用了NLog作为日志记录工具,努得到安装命令:

安装-打包NLog安装-打包NLog .配置相比Log4net,NLog配置简单,仅几行代码即可,NLog.config:

?可扩展标记语言版本='1.0 '编码='utf-8 '?nlog xmlns=' http://www。nlog-项目。组织/架构/nlog。xsd ' xmlns : xsi=' http://www .w3。org/2001/XMLSchema-instance ' targets xsi 3360 type=' File ' name=' f ' fileName=' $ { basedir }/log/$ { short date } .log ' layout=' $ {大写: $ { level } } $ {长日期} $ { message } '/target xsi : type=' File ' name=' F2 ' fileName=' d : log mvce扩展名 $ {短日期} .log ' layout=' $ {大写: $ { level } } $ {长日期} $ { message } '//targets rules logger name=' * ' min level=' Debug ' write to=' F2 '/rules/nlog如果报错,日志就记录在D盘的原木目录下的多视图扩展目录下,一个项目一个日志目录,方便管理。全部配置完成,看下代码:

public JsonResult HandleErrorFilterTest(){ int I=int .分析(“ABC”);返回Json(新的返回模型_ Data { Data=I });}字符串强转成(同国际组织)国际组织类型,必然报错,页面响应:

同时日志也记录下来了:

3.ActionFilter自定义过滤器

自定义过滤器就更加灵活了,可以精确的注入到请求前、请求中和请求后。继承抽象类ActionFilterAttribute并重写里面的方法即可:

公共类SystemLogAttribute : ActionFilterAttribute {公共字符串操作{ get设置;} public override void onationexecuted(ActionExecutedContext筛选器上下文){筛选器上下文.HttpContext。回应。write(' br/' Operate ':onationexecuted ');基地. OnActionExecuted(筛选器上下文);} public override void onationexecuting(action executing context filter context){ filter context .HttpContext。回应。write(' br/' Operate ':onationexecuting ');基地。操作执行(过滤器上下文);} public override void on resultexecuted(ResultExecutedContext筛选器上下文){筛选器上下文.HttpContext。回应。写入(' br/' Operate ':OnResultExecuted ');基地OnResultExecuted(filterContext);}对ResultExecutingContext筛选器上下文{ filter context }执行公共重写void。HttpContext。回应。写入(' br/' Operate ':OnResultExecuting ');基地OnResultExecuting(filterContext);}}这个过滤器适合做系统操作日志记录功能:

[系统日志(操作='添加用户)]公共字符串CustomerFilterTest(){响应.写入(' br/操作执行中.');返回br/操作执行结束;}看下结果:

四个方法执行顺序:操作执行—操作执行—操作执行—操作执行—操作执行,非常精确的控制了整个请求过程。

实战中记录日志过程是这样的:在操作执行方法里写一条操作日志到数据库里,全局变量存下这条记录的主键,到OnResultExecuted方法里说明请求结束了,这个时候自然知道用户的这个操作是否成功了,根据主键更新下这条操作日志的是否成功字段。

二、模型绑定(模型绑定器)

先看一个普通的方法:

公共行动结果索引(学生学生){返回视图();}这个方法接受的参数是一个学生对象,前端传递过来的参数跟学生对象里的属性保持一直,那么就自动被绑定到这个对象里了,不需要在方法里新生这个对象并挨个绑定属性了,绑定的过程由手动音量调节中的DefaultModelBinder完成的,DefaultModelBinder同时继承了IModelBinder接口,现在就利用IModelBinder接口和DefaultModelBinder来实现更加灵活的模型绑定。

场景一、前端传过来了一个加密的字符串令牌,方法里需要用代币里的某些字段,那就得在方法里接收这个字符串、解密字符串、转换成对象,这样一个方法还好说,多了的话重复代码非常多,就算提取通用方法,还是要在方法里调用这个通用方法,有没有办法直接在参数里就封装好这个对象?

模型绑定的对象:

公共类TokenModel{ ///summary ///主键////summary public int Id { get;设置;} ///摘要///姓名////摘要公共字符串名称{集合得到;} ///摘要///简介////摘要公共字符串描述{ get设置;}}新建一个令牌绑定器继承IModelBinder接口并实现其中的BindModel方法:

公共类令牌绑定器: IModelBinder{公共对象绑定模型(控制器上下文控制器上下文,ModelBindingContext绑定上下文){ var token=控制器上下文.http上下文。请求['令牌'];if(!字符串IsNullOrEmpty(token)){ string[]array=token .拆分(' : ');如果(数组长度==3){ 0返回新的TokenModel() { Id=int .解析(数组[0]),名称=数组[1],说明=数组[2]};} else {返回新的令牌模型(){ Id=0 };} } else {返回新的令牌模型(){ Id=0 };} }}这个方法里接收了一个代币参数,并对代币参数进行了解析和封装。代码部分完成了需要到应用程序_开始方法里进行下注册:

模型绑定。绑定器。添加(类型(令牌模型),新令牌绑定器());现在模拟下这个接口:

public JsonResult TokenBinderTest(令牌模型令牌模型){ var输出='Id:' tokenModel .标识,名称:'令牌模型。名称,描述:'令牌模型。描述;返回Json(新的返回模型_ Common { msg=output });}调用下:

可以看出,"1:汪杰:oppoic.cnblogs.com "已经被绑定到令牌模型这个对象里面了。但是如果稍复杂的模型绑定IModelBinder就无能为力了。

场景二、去除对象某个属性的首位空格

公共类学生{ public int Id { get设置;}公共字符串名称{获取设置;}公共字符串类{获取设置;}}如果前端传来的名字属性有空格,如何去除呢?利用DefaultModelBinder即可实现更灵活的控制

公共类TripMoldElbinder : DefaultMoldElbinder {受保护的覆盖对象getPropertyValue(控制器上下文控制器上下文,ModelBindingContext,BindingContext,PropertyDescriptor,iMoldElbinder属性绑定器){ var obj=base .GetPropertyValue(controllerContext,bindingContext,propertyDescriptor,property binder);如果(obj是字符串属性描述符。属性[类型为(TriMatAttribute)]!=null)//判断是线类型且有[修剪]标记{返回(obj为字符串)。trim();}返回obj}}标注下需要格式化首位属性的实体:

[MoDEL活页夹(类型(TripMoDEL活页夹))]公共类学生{ public int Id { get设置;}[修剪]公共字符串名称{获取设置;}公共字符串类{获取设置;}}好了,测试下:

公共JsonResult trimbindetest(Student Student){ if(string .IsNullOrEmpty(学生。名称)||字符串IsNullOrEmpty(学生。类){ return Json(新的返回模型_ Common { msg='未找到参数' });} else { return Json(新的返回模型_ Common { msg=' Name ':学生.姓名,长度:'学生。姓名。长度'班级:学生。类,长度:'学生班级。length });}}可见,标注了附加装饰属性的名字长度是去除空格的长度:7,而没有标注的班级属性的长度则是6。

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

更多资讯
游戏推荐
更多+