在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理。引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想,通过Filter能统一地对一些通用逻辑进行处理,如:权限校验、参数加解密、参数校验等方面我们都可以利用这一特性进行统一处理,今天我们来介绍Filter的开发、使用以及讨论他们的执行顺序。

一、Filter的开发和调用

在默认的WebApi中,框架提供了三种Filter,他们的功能和运行条件如下表所示:

Filter 类型

实现的接口

描述

Authorization

IAuthorizationFilter

最先运行的Filter,被用作请求权限校验

Action

IActionFilter

在Action运行的前、后运行

Exception

IExceptionFilter

当异常发生的时候运行

首先,我们实现一个AuthorizatoinFilter可以用以简单的权限控制:

    public class AuthFilterAttribute : AuthorizationFilterAttribute{        public override void OnAuthorization(HttpActionContext actionContext){            //如果用户方位的Action带有AllowAnonymousAttribute,则不进行授权验证if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()){                return;}            var verifyResult = actionContext.Request.Headers.Authorization!=null &&  //要求请求中需要带有Authorization头actionContext.Request.Headers.Authorization.Parameter == "123456"; //并且Authorization参数为123456则验证通过if (!verifyResult){                //如果验证不通过,则返回401错误,并且Body中写入错误原因actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized,new HttpError("Token 不正确"));}}    }

一个简单的用于用户验证的Filter就开发完了,这个Filter要求用户的请求中带有Authorization头并且参数为123456,如果通过则放行,不通过则返回401错误,并在Content中提示Token不正确。下面,我们需要注册这个Filter,注册Filter有三种方法:

第一种:在我们希望进行权限控制的Action上打上AuthFilterAttribute这个Attribute:

    public class PersonController : ApiController{[AuthFilter]        public CreateResult Post(CreateUser user){            return new CreateResult() {Id = "123"};}}

这种方式适合单个Action的权限控制。

第二种,找到相应的Controller,并打上这个Attribute:

    [AuthFilter]        public class PersonController : ApiController{        public CreateResult Post(CreateUser user){            return new CreateResult() {Id = "123"};}}

这种方式适合于控制整个Controller,打上这个Attribute以后,整个Controller里所有Action都获得了权限控制。

第三种,找到App_Start\WebApiConfig.cs,在Register方法下加入Filter实例:

public static void Register(HttpConfiguration config)
{     config.MapHttpAttributeRoutes();    //注册全局Filterconfig.Filters.Add(new AuthFilterAttribute());config.Routes.MapHttpRoute(name: "DefaultApi",routeTemplate: "api/{controller}/{id}",defaults: new { id = RouteParameter.Optional });
}

用这种方式适合于控制所有的API,任意Controller和任意Action都接受了这个权限控制。

在大多数场景中,每个API的权限验证逻辑都是一样的,在这样的前提下使用全局注册Filter的方法最为简单便捷,可这样存在一个显而易见的问题:如果某几个API是不需要控制的(例如登录)怎么办?我们可以在这样的API上做这样的处理:

[AllowAnonymous]public CreateResult PostLogin(LoginEntity entity)
{      //TODO:添加验证逻辑return new CreateResult() {Id = "123456"};
}

我为这个Action打上了AllowAnonymousAttribute,验证逻辑就放过了这个API而不进行权限校验。

在实际的开发中,我们可以设计一套类似Session的机制,通过用户登录来获取Token,在之后的交互HTTP请求中加上Authorization头并带上这个Token,并在自定义的AuthFilterAttribute中对Token进行验证,一套标准的Token验证流程就可以实现了。

接下来我们介绍ActionFilter:

  ActionFilterAttrubute提供了两个方法进行拦截:OnActionExecuting和OnActionExecuted,他们都提供了同步和异步的方法。

  OnActionExecuting方法在Action执行之前执行,OnActionExecuted方法在Action执行完成之后执行。

  我们来看一个应用场景:使用过MVC的同学一定不陌生MVC的模型绑定和模型校验,使用起来非常方便,定义好Entity之后,在需要进行校验的地方可以打上相应的Attribute,在Action开始时检查ModelState的IsValid属性,如果校验不通过直接返回View,前端可以解析并显示未通过校验的原因。而Web API中也继承了这一方便的特性,使用起来更加方便:

 public class CustomActionFilterAttribute : ActionFilterAttribute
{    public override void OnActionExecuting(HttpActionContext actionContext){        if (!actionContext.ModelState.IsValid){actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest,  actionContext.ModelState);}}
}

这个Filter就提供了模型校验的功能,如果未通过模型校验则返回400错误,并把相关的错误信息交给调用者。他的使用方法和AuthFilterAttribute一样,可以针对Action、Controller、全局使用。我们可以用下面一个例子来验证:

代码如下:

public class LoginEntity
{[Required(ErrorMessage = "缺少用户名")]    public string UserName { get; set; }[Required(ErrorMessage = "缺少密码")]    public string Password { get; set; }
}
[AllowAnonymous]
[CustomActionFilter]public CreateResult PostLogin(LoginEntity entity)
{     //TODO:添加验证逻辑return new CreateResult() {Id = "123456"};
}

当然,你也可以根据自己的需要解析ModelState然后用自己的格式将错误信息通过Request.CreateResponse()返回给用户。

  OnActionExecuted方法我在实际工作中使用得较少,目前仅在一次部分响应数据加密的场景下进行过使用,使用方法一样,读取已有的响应,并加密后再给出加密后的响应赋值给actionContext.Response即可。

我给大家一个Demo:

public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{        var key = 10;        var responseBody = await actionExecutedContext.Response.Content.ReadAsByteArrayAsync(); //以Byte数组方式读取Content中的数据for (int i = 0; i < responseBody.Length; i++){responseBody[i] = (byte)(responseBody[i] ^ key); //对每一个Byte做异或运算        }actionExecutedContext.Response.Content = new ByteArrayContent(responseBody); //将结果赋值给Response的ContentactionExecutedContext.Response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("Encrypt/Bytes"); //并修改Content-Type}

  通过这个方法我们将响应的Content每个Byte都做了一个异或运算,对响应内容进行了一次简单的加密,大家可以根据自己的需要进行更可靠的加密,如AES、DES或者RSA…通过这个方法可以灵活地对某个Action的处理后的结果进行处理,通过Filter进行响应内容加密有很强的灵活性和通用性,他能获取当前Action的很多信息,然后根据这些信息选择加密的方式、获取加密所需的参数等等。如果加密所使用参数对当前执行的Action没有依赖,也可以采取HttpMessageHandler来进行处理,在之后的教程中我会进行介绍。

最后一个Filter:ExceptionFilter

顾名思义,这个Filter是用来进行异常处理的,当业务发生未处理的异常,我们是不希望用户接收到黄页或者其他用户无法解析的信息的,我们可以使用ExceptionFilter来进行统一处理:

public class ExceptionFilter : ExceptionFilterAttribute
{    public override void OnException(HttpActionExecutedContext actionExecutedContext){        //如果截获异常为我们自定义,可以处理的异常则通过我们自己的规则处理if (actionExecutedContext.Exception is DemoException){            //TODO:记录日志actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.BadRequest, new {Message = actionExecutedContext.Exception.Message});}        else{            //如果截获异常是我没无法预料的异常,则将通用的返回信息返回给用户,避免泄露过多信息,也便于用户处理            //TODO:记录日志actionExecutedContext.Response =actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError,                        new {Message = "服务器被外星人拐跑了!"});}}
}

我们定义了一个ExceptoinFilter用于处理未捕获的异常,我们将异常分为两类:一类是我们可以预料的异常:如业务参数错误,越权等业务异常;还有一类是我们无法预料的异常:如数据库连接断开、内存溢出等异常。我们通过HTTP Code告知调用者以及用相对固定、友好的数据结构将异常信息告诉调用者,以便于调用者记录并处理这样的异常。

 [CustomerExceptionFilter]public class TestController : ApiController
{    public int Get(int a, int b){        if (a < b){            throw new DemoException("A必须要比B大!");}        if (a == b){            throw new NotImplementedException();}        return a*b;}
}

我们定义了一个Action:在不同的情况下会抛出不同的异常,其中一个异常是我们能够预料并认为是调用者传参出错的,一个是不能够处理的,我们看一下结果:

在这样的RestApi中,我们可以预先定义好异常的表现形式,让调用者可以方便地判断什么情况下是出现异常了,然后通过较为统一的异常信息返回方式让调用者方便地解析异常信息,形成统一方便的异常消息处理机制。

    但是,ExceptionFilter只能在成功完成了Controller的初始化以后才能起到捕获、处理异常的作用,而在Controller初始化完成之前(例如在Controller的构造函数中出现了异常)则ExceptionFilter无能为力。对此WebApi引入了ExceptionLogger和ExceptionHandler处理机制,我们将在之后的文章中进行讲解。

二、Filter的执行顺序

在使用MVC的时候,ActionFilter提供了一个Order属性,用户可以根据这个属性控制Filter的调用顺序,而Web API却不再支持该属性。Web API的Filter有自己的一套调用顺序规则:

所有Filter根据注册位置的不同拥有三种作用域:Global、Controller、Action:

通过HttpConfiguration类实例下Filters.Add()方法注册的Filter(一般在App_Start\WebApiConfig.cs文件中的Register方法中设置)就属于Global作用域;

通过Controller上打的Attribute进行注册的Filter就属于Controller作用域;

通过Action上打的Attribute进行注册的Filter就属于Action作用域;

他们遵循了以下规则:

1、在同一作用域下,AuthorizationFilter最先执行,之后执行ActionFilter

2、对于AuthorizationFilter和ActionFilter.OnActionExcuting来说,如果一个请求的生命周期中有多个Filter的话,执行顺序都是Global->Controller->Action;

3、对于ActionFilter,OnActionExecuting总是先于OnActionExecuted执行;

4、对于ExceptionFilter和ActionFilter.OnActionExcuted而言执行顺序为Action->Controller->Global;

5、对于所有Filter来说,如果阻止了请求:即对Response进行了赋值,则后续的Filter不再执行。

关于默认情况下的Filter相关知识我们就讲这么一些,如果在文章中有任何不正确的地方或者疑问,欢迎大家为我指出。

相关文章:

  • WebAPI前置知识:HTTP与RestfulAPI

  • WEB API系列(一):WEB API的适用场景、第一个实例

  • ASP.NET Web API 安全筛选器

  • Web API 基于ASP.NET Identity的Basic Authentication

  • ASP.NET Web Api使用CacheCow和ETag缓存资源

  • 使用 West Wind WebSurge 对 ASP.NET Web API 服务进行压力测试

  • Web API应用支持HTTPS的经验总结

  • Dion Hinchcliffe谈Web API的过去与未来

原文地址:http://www.cnblogs.com/UliiAn/p/5402146.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

WEB API 系列(二) Filter的使用以及执行顺序相关推荐

  1. 【异步系列二】Promise原理及执行顺序详解

    前言 Promise 是 javascript 中非常重要的一环,熟悉它是必须的,而且在面试中也常常会问到相关面试题. 在了解 Promise 之前,需要了解什么是异步编程,可以参考我的一篇文章:Ja ...

  2. Web API系列(三)统一异常处理

    前面讲了webapi的安全验证和参数安全,不清楚的朋友,可以看看前面的文章,<Web API系列(二)接口安全和参数校验>,本文主要介绍Web API异常结果的处理.作为内部或者是对外提供 ...

  3. Web API系列(三):添加接口详细说明及测试

    1.引言 前边两篇博客介绍了Web API的基本框架以及路由配置,这篇博客主要解决在前后端分离项目中,为前端人员提供详细接口说明的问题,主要是通过修改WebApi HelpPage相关代码和添加Web ...

  4. Web API系列之三 基本功能实现

    Web API系列之二讲解了如何搭建一个WebApi的基架,本文主要在其基础之上实现基本的功能.下面开始逐步操作: 一.配置WebApi的路由-用于配置外部如何访问内部资源的url的规则 1.添加Gl ...

  5. [转]Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务

    本文转自:http://www.cnblogs.com/fzrain/p/3923727.html 前言 很久没更新了,之前有很多事情,所以拖了很久,非常抱歉.好了,废话不多说,下面开始正题.本篇仍然 ...

  6. WEB API系列(一):WEB API的适用场景、第一个实例

    在我前一篇博客<WebAPI前置知识:HTTP与RestfulAPI>中已经给各位简单介绍了HTTP协议与RestFul API的关系,以及一些基本的HTTP协议知识,在这些知识的铺垫下, ...

  7. WEB API Filter的使用以及执行顺序

    在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don't Repeat Yourself)思想 ...

  8. web api 开发之 filter

    1.使用filter之前应该知道的(不知道也无所谓,哈哈!)     谈到filter 不得不先了解下aop(Aspect Oriented Programming)面向切面的编程.(度娘上关于aop ...

  9. webapi 设置参数可为空_Web API系列(二):灵活多样的路由配置

    1.导言 路由系统是请求消息进入http://ASP.NET Web API消息处理管道的第一道屏障,其根本目的在于利用注册的路由对请求的URL进行解析以确定目标HTTPController和Acti ...

最新文章

  1. 思维dp ---- 复杂状态找中间状态作为dp状态 1552F - Telepanting
  2. windows installer清理实用工具
  3. nvidia显示设置不可用_白送大福利?实战FreeSync显示器开启NVIDIA GSync Compatible
  4. 嵌入式面试中常见的编程题目
  5. LAMP_ 访问控制
  6. 关于大规模录入的数据流转
  7. Sandbox简介和路径获取
  8. spss20安装许可证代码_SPSS23安装教程
  9. 安科瑞电力监控系统在上海平凉街道23街坊商办项目的应用
  10. Linux查找文件路径
  11. Servlet学习记录2
  12. 正则表达式 压缩 HTML 字符串
  13. Android程序猿基本功
  14. 关闭烦人的wps广告
  15. java搭建直播商城VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城
  16. 【UE5】蓝图制作简单地雷教程
  17. 恶意软件免杀与技术(2022.10.08)
  18. 服务器崩溃 文件备份,如果备份服务器崩溃了
  19. 中国乳房X射线照相设备市场趋势报告、技术动态创新及市场预测
  20. 除了SVN,还可以使用的文档管理工具--TFS; 使用VS2010连接TFS的使用说明和简单操作手册

热门文章

  1. 配置Tomcat的日志系统
  2. [SIR数据集实验][2]Java类数据集相应工具使用的小经验
  3. C# datagridview、datagrid、GridControl增加行号
  4. 在VS Code中执行SQL查询,是怎样一种体验?
  5. 开源的负载测试/压力测试工具 NBomber
  6. ML.NET Cookbook:(7)如何训练回归模型?
  7. NET问答: Find() 和 Where().FirstOrDefault() 该如何选择 ?
  8. 牛X,.NET6又双叒叕新版本,这是要起飞吗?
  9. C#8.0宝藏好物Async streams
  10. 读书 | IT人如何直击本质洞察底层逻辑?