WEB APIではアスペクト指向プログラミング(AOP)の考え方が導入されており、プロセスインターセプト処理のために特定の場所に特定のフィルターを挿入することができます。このメカニズムの導入により、フィルターを通じて、許可の検証、パラメーターの暗号化と復号化、パラメーターの検証などのいくつかの一般的なロジックを均一に処理できるようになり、DRY (Don't Reply Yourself) のアイデアをより適切に実装できるようになります。今日は、Filter の開発と使用法を紹介し、その実行シーケンスについて説明します。
1. Filter の開発と呼び出し
デフォルトの WebApi では、フレームワークは次の表に示す 3 種類の Filter を提供します。その機能と動作条件は次の表のとおりです。
実装されたインターフェース |
説明 |
Authorization |
最初に実行するもの Filter |
、リクエスト許可の検証として使用 | Action |
Aアクション |
前後の操作 | Exception | IExceptionFilter
例外が発生したときに実行します |
まず、単純な権限を制御するために使用できる Authorizatoinfilter を実装します。
(actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute> verifyResult = actionContext.Request.Headers.Authorization!= && == ; (!= HttpError( ログイン後にコピー public class PersonController : ApiController { [AuthFilter] public CreateResult Post(CreateUser user) { return new CreateResult() {Id = "123"}; } } ログイン後にコピー [AuthFilter] public class PersonController : ApiController { public CreateResult Post(CreateUser user) { return new CreateResult() {Id = "123"}; } } ログイン後にコピー { id = ログイン後にコピー [AllowAnonymous]public CreateResult PostLogin(LoginEntity entity) { //TODO:添加验证逻辑 return new CreateResult() {Id = "123456"}; } ログイン後にコピー public class CustomActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ModelState.IsValid) { actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState); } } } ログイン後にコピー 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"}; } ログイン後にコピー 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的Content actionExecutedContext.Response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("Encrypt/Bytes"); //并修改Content-Type} ログイン後にコピー 顾名思义,这个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相关知识我们就讲这么一些,如果在文章中有任何不正确的地方或者疑问,欢迎大家为我指出。 |
以上がAsp.Net WebAPI でのフィルターの使用と実行シーケンス (お気に入り)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。