网站营销的优势,建设网站有哪些术语,长春人才招聘网官网,360建筑网忘记登入密码了怎么办过滤器是 ASP.NET Core 中的特殊组件#xff0c;允许我们在请求管道的特定阶段控制请求的执行。这些过滤器在中间件执行后以及 MVC 中间件匹配路由并调用特定操作时发挥作用。 简而言之#xff0c;过滤器提供了一种在操作级别自定义应用程序行为的方法。它们就像检查点#… 过滤器是 ASP.NET Core 中的特殊组件允许我们在请求管道的特定阶段控制请求的执行。这些过滤器在中间件执行后以及 MVC 中间件匹配路由并调用特定操作时发挥作用。 简而言之过滤器提供了一种在操作级别自定义应用程序行为的方法。它们就像检查点允许我们执行特定任务例如异常处理、缓存或添加自定义响应标头。 当请求到达某个操作时过滤器可以收集有关已选择哪个操作以及关联的路由数据的信息。此信息可用于做出决策并执行特定于该特定操作或控制器的操作。 通过使用过滤器我们在处理请求时拥有更多的控制权和灵活性。我们可以根据我们的要求实现自定义逻辑并将特定行为应用于不同的操作或控制器。这有助于在 ASP.NET Core 应用程序中实现更加定制和高效的请求处理流程。 过滤器的类型 ASP.NET Core 提供了一组预定义的过滤器类型允许我们在操作或控制器的上下文中控制请求的执行。这些过滤器有不同的用途可以根据我们的要求进行定制。 以下是 ASP.NET Core 中预定义过滤器的主要类型 AuthorizationFilter此过滤器在路由执行开始时执行。它确定是否允许用户访问该路由或执行请求的操作。 ResourceFilter该过滤器在授权后运行可用于绕过剩余管道的执行。如果预处理的响应可用我们想要发送缓存的响应从而跳过管道的其余部分那么它非常有用。 ActionFilter动作过滤器在动作执行之前和之后运行。它提供了一种用自定义逻辑包围操作或在执行操作之前和之后执行其他任务的方法。 ResultFilter此过滤器在操作生成结果之前和之后执行。它允许我们用附加行为包围结果执行或根据结果执行某些操作。 ExceptionFilter每当操作或控制器中抛出未捕获的异常时异常过滤器就会运行。它提供了一种处理异常并设计特定于操作或控制器的自定义错误响应的方法。 ServiceFilter此过滤器运行另一个过滤器其类型在其自身内传递。当传递的过滤器类型注册为服务时使用它。它解决通过依赖项注入 (DI) 传递给过滤器类型的任何依赖项。 TypeFilter与服务过滤器类似类型过滤器也运行未注册为服务的过滤器类型。它允许我们应用自定义过滤器而无需注册。 这些预定义的过滤器类型使我们能够灵活地控制请求执行过程并在 ASP.NET Core 应用程序的管道的不同阶段添加特定行为。 过滤器范围 ASP.NET Core 中过滤器的范围取决于它们附加到 MVC 管道的方式。这使我们能够控制过滤器执行的时间和地点。过滤器的主要范围分为三个 特定于操作的范围过滤器可以应用于控制器内的特定操作方法。通过使用过滤器属性修饰操作过滤器将仅在选择该特定操作来处理传入请求时执行。 特定于控制器的范围过滤器也可以应用于整个控制器类。当过滤器应用于控制器级别时它将针对该控制器内的所有操作执行。 全局范围全局过滤器应用于路由中间件匹配和拾取的每条路由。这些过滤器被注册为全局过滤器并且无论特定操作或控制器如何都会被执行。但是如果没有为请求选择端点则不会执行任何过滤器。 要将过滤器注册为全局过滤器可以将其添加到 Startup 类中的 ConfigureServices() 方法内的过滤器数组中。这是一个例子 services.AddControllers(options { options.Filters.Add(typeof(ConsoleGlobalActionFilter)); }); 在此示例中使用 Add(typeof(...)) 方法将 ConsoleGlobalActionFilter 注册为全局筛选器。这确保过滤器将应用于路由中间件匹配和处理的每个路由。 通过控制过滤器的范围我们可以精确地确定过滤器将在 ASP.NET Core 应用程序中应用的时间和位置从而允许我们添加特定的行为并有效地控制请求执行过程。 如何创建简单的过滤器 有两种方法可以创建我们之前讨论的类型的过滤器。我们以创建ActionFilter为例 实现接口要创建根据操作执行进行操作的自定义操作过滤器我们可以创建一个实现 IActionFilter 或 IAsyncActionFilter 接口的类。这些接口提供了我们可以重写以添加自定义逻辑的方法。通过实现这些接口我们可以完全控制操作过滤器的行为。 扩展属性类或者我们可以创建一个扩展 ActionFilterAttribute 类的自定义操作过滤器类。ActionFilterAttribute 类已经实现了 IActionFilter 和 IAsyncActionFilter 接口。通过扩展此类我们可以重写我们感兴趣的方法并添加自定义逻辑。这种方法提供了一种更方便的方法来创建操作过滤器因为我们可以直接扩展基类并专注于实现必要的方法。 同样对于提到的其他类型的过滤器如授权过滤器、资源过滤器、结果过滤器、异常过滤器也有相应的可以实现的接口或可以扩展的属性类。 以下是上述过滤器的接口和属性类 IAuthorizationFilter可以通过AuthorizationFilterAttribute实现或扩展。 IResourceFilter可以通过ResourceFilterAttribute实现或扩展。 IActionFilter可以通过ActionFilterAttribute实现或扩展。 IResultFilter可以通过ResultFilterAttribute实现或扩展。 IExceptionFilter可以通过ExceptionFilterAttribute实现或扩展。 其中一些过滤器附带有实现相应接口的属性类使我们能够灵活地重写适合我们需要的特定方法。以下是一些过滤器的属性类 ActionFilterAttribute该属性类实现 IActionFilter 和 IAsyncActionFilter 接口。通过扩展这个类我们可以直接继承这些接口的实现并重写我们感兴趣的方法。这使得我们可以根据我们的需求自定义动作过滤器的行为。 ExceptionFilterAttribute该属性类实现 IExceptionFilter 接口。通过扩展此类并重写其方法我们可以处理和自定义对操作或控制器中发生的异常的处理。这使我们能够控制异常的处理方式并允许我们提供自定义错误响应。 ResultFilterAttribute该属性类实现 IResultFilter 接口。通过扩展此类并重写其方法我们可以在将操作生成的结果发送回客户端之前对其进行修改。这允许我们向结果添加额外的处理或转换。 FormatFilterAttribute此属性类用于指定操作或控制器支持的响应格式。它有助于内容协商并根据客户的偏好选择适当的响应格式。 ServiceFilterAttribute此属性类用于应用注册为服务的过滤器。它允许我们使用依赖项注入来解决过滤器所需的任何依赖项。当过滤器需要额外的服务或依赖项才能正常运行时这会很有帮助。 TypeFilterAttribute该属性类与ServiceFilterAttribute类似但它允许我们应用未注册为服务的过滤器。我们可以直接指定过滤器类型及其依赖项。 例如ActionFilterAttribute类已经为我们实现了IActionFilter和IAsyncActionFilter接口因此我们可以直接扩展ActionFilterAttribute类并重写我们感兴趣的方法。 using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json; public class AddResponseHeaderFilter : ActionFilterAttribute
{ // Async method which can surround the action execution// Invoked before and after the action executionpublic asyn coverride Task OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next) { // Access the request context.HttpContext.Response.Headers.Add( X-MyCustomHeader, Guid.NewGuid().ToString()); var result await next.Invoke(); // Access the response Console.WriteLine(JsonConvert.SerializeObject(result.Result)); }
} [Route(api/[controller])]
[ApiController]
public class HomeController : ControllerBase
{ [AddResponseHeaderFilter] [Route()] [HttpGet] public IActionResult Index() { return Ok(new { Message Im Alive }); }
} 在此示例中我们通过扩展ActionFilterAttribute创建了自定义AddResponseHeaderFilter类。此过滤器在执行操作之前和之后向 HTTP 响应添加自定义响应标头。它重写OnActionExecutionAsync方法该方法允许我们自定义操作执行周围的行为。 然后我们使用 [ AddResponseHeaderFilter ] 属性将AddResponseHeaderFilter过滤器应用到HomeController类的Index操作。这可确保针对该特定操作执行过滤器。 调用该操作时过滤器使用Response.Headers.Add方法将自定义标头添加到响应中。然后它调用 next.Invoke() 方法来继续执行操作。执行操作后它会访问响应结果并使用JsonConvert.SerializeObject 将其写入控制台。 通过利用ActionFilterAttribute并创建自定义过滤器类我们可以扩展和自定义 ASP.NET Core 请求管道的行为以添加其他功能并根据我们的要求修改响应。 动作过滤器的实现 示例 1实现 IActionFilter 的同步操作过滤器
using Microsoft.AspNetCore.Mvc.Filters;namespace ActionFilters.Filters
{public class ActionFilterExample : IActionFilter{public void OnActionExecuting(ActionExecutingContext context){// Code executed before the action method executes}public void OnActionExecuted(ActionExecutedContext context){// Code executed after the action method executes}}
} 在此示例中我们创建一个名为 ActionFilterExample 的类该类实现 IActionFilter 接口。该接口要求我们实现两个方法OnActionExecuting 和 OnActionExecuted。OnActionExecuting 方法包含将在操作方法之前执行的代码而 OnActionExecuted 方法包含将在操作方法之后执行的代码。
示例 2实现 IAsyncActionFilter 的异步操作过滤器
using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;namespace ActionFilters.Filters
{public class AsyncActionFilterExample : IAsyncActionFilter{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){// Code executed before the action method executesvar result await next();// Code executed after the action method executes}}
} 在此示例中我们创建一个名为 AsyncActionFilterExample 的类该类实现 IAsyncActionFilter 接口。该接口要求我们实现 OnActionExecutionAsync 方法该方法是动作过滤器的异步版本。该方法接收一个 ActionExecutingContext 和一个 ActionExecutionDelegate。ActionExecutionDelegate 代表管道中的下一个操作我们可以等待它执行操作方法。通过这样做我们可以在操作方法执行之前和之后运行代码。 通过创建实现 IActionFilter 或 IAsyncActionFilter 的类我们可以定义要在 ASP.NET Core 中的操作方法之前和之后执行的自定义逻辑。这些操作过滤器提供了一种向请求管道添加附加行为并根据需要修改请求或响应的方法。 操作过滤器的范围 可以在不同范围级别添加操作过滤器全局、操作和控制器。 1. 全局范围 要全局使用操作过滤器您可以在ConfigureServices方法的AddControllers()方法中注册它 public void ConfigureServices(IServiceCollection services)
{services.AddControllers(config {config.Filters.Add(new GlobalFilterExample());});
}
在.NET 6及更高版本中由于缺少 Startup 类您可以使用 Program 类
builder.Services.AddControllers(config
{ config.Filters.Add( new GlobalFilterExample());
}); 通过将过滤器添加到全局范围它将应用于整个应用程序中的所有操作方法。 2. 操作和控制器范围 如果要在操作或控制器级别使用操作过滤器则需要在ConfigureServices方法中将其注册为IoC容器中的服务 services.AddScopedActionFilterExample();
services.AddScopedControllerFilterExample();
在 .NET 6 及更高版本中
builder.Services.AddScopedActionFilterExample();
builder.Services.AddScopedControllerFilterExample(); 通过将操作过滤器注册为服务您可以通过使用相应的过滤器属性装饰它们有选择地将其应用到特定的操作方法或控制器。 最后要使用在操作或控制器级别注册的过滤器您需要使用ServiceFilter属性将其应用到相应的控制器或操作方法之上 namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample))][Route(api/[controller])][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample))]public IEnumerablestring Get(){return new string[] { example, data };}}
} 通过使用[ServiceFilter]属性并指定过滤器类型您可以将注册的过滤器ControllerFilterExample 和 ActionFilterExample分别与控制器和操作方法关联起来。 这允许将过滤器应用于指定的范围控制执行流程并在执行操作方法之前和之后提供附加功能。 以这种方式使用ServiceFilter属性可以帮助您利用依赖项注入的优势并将所需的过滤器应用到 ASP.NET 应用程序中的特定控制器和操作方法。 调用顺序 我们的过滤器的执行顺序如下 我们可以通过向ServiceFilter属性添加一个名为Order 的附加属性来更改多个过滤器的调用顺序
namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample), Order2)][Route(api/[controller])][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample), Order1)]public IEnumerablestring Get(){return new string[] { example, data };}}
} 在本例中ControllerFilterExample将在ActionFilterExample之前执行因为它们的顺序值分别为 2 和 1。通过为过滤器分配不同的顺序值您可以控制它们的执行顺序。 您还可以将多个过滤器应用于同一操作方法并定义它们的执行顺序 [HttpGet]
[ServiceFilter(typeof(ActionFilterExample), Order2)]
[ServiceFilter(typeof(ActionFilterExample2), Order1)]
public IEnumerablestring Get()
{ return new string[] { example, data ” };
} 在这种情况下ActionFilterExample2将根据其顺序值分别为 1 和 2在ActionFilterExample之前执行。 通过指定每个过滤器的顺序您可以对执行顺序进行细粒度控制并且可以应用具有不同优先级的多个过滤器以在 ASP.NET 应用程序中实现所需的行为。 使用操作过滤器改进代码 为了使用操作过滤器改进代码让我们重点关注存储库中AppStart文件夹中起始项目的Controllers文件夹中的MoveController类。该控制器包含所有 CRUD 操作的实现。
尽管我们的操作已经干净且可读但由于全局异常处理我们可以进一步增强它们。
需要注意的一件重要事情是我们的Movie模型继承自IEntity接口:
[Table(Movie)]
public class Movie: IEntity
{[Key]public Guid Id { get; set; }[Required(ErrorMessage Name is required)]public string Name { get; set; }[Required(ErrorMessage Genre is required)]public string Genre { get; set; }[Required(ErrorMessage Director is required)]public string Director { get; set; }
} 现在让我们关注POST和PUT操作的验证代码。通过将适当的操作过滤器应用于POST和PUT操作我们可以消除这些操作中显式验证代码的需要使它们更加简洁和可维护。
使用操作过滤器进行验证 为了改进POST和PUT操作中的验证代码我们可以使用操作过滤器。通过将验证逻辑提取到自定义操作过滤器中我们可以使代码更具可重用性并保持操作更清晰。 首先我们在解决方案资源管理器中创建一个名为“ActionFilters”的新文件夹。在该文件夹中创建一个名为ValidationFilterAttribute的新类
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;namespace ActionFilters.ActionFilters
{public class ValidationFilterAttribute : IActionFilter{public void OnActionExecuting(ActionExecutingContext context){var param context.ActionArguments.SingleOrDefault(p p.Value is IEntity);if (param.Value null){context.Result new BadRequestObjectResult(Object is null);return;}if (!context.ModelState.IsValid){context.Result new UnprocessableEntityObjectResult(context.ModelState);}}public void OnActionExecuted(ActionExecutedContext context){ }}
} 在ValidationFilterAttribute类中我们重写OnActionExecuting方法来执行验证逻辑。我们检查操作参数是否为 IEntity 类型如果为 null则返回BadRequestObjectResult。此外如果 ModelState 无效我们将返回UnprocessableEntityObjectResult。 接下来让我们在ConfigureServices方法中将此操作过滤器注册为服务 public void ConfigureServices(IServiceCollection services)
{services.AddDbContextMovieContext(options options.UseSqlServer(Configuration.GetConnectionString(sqlConString)));services.AddScopedValidationFilterAttribute();services.AddControllers();
}
对于.NET 6我们需要在 Program 类中使用构建器变量
builder.Services.AddDbContextMovieContext(options options.UseSqlServer(Configuration.GetConnectionString( sqlConString ))); builder.Services.AddScopedValidationFilterAttribute(); builder.Services.AddControllers();
最后从 POST 和 PUT 操作中删除验证代码并将ValidationFilterAttribute作为服务应用
[HttpPost]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Post([FromBody] Movie movie)
{_context.Movies.Add(movie);_context.SaveChanges();return CreatedAtRoute(MovieById, new { id movie.Id }, movie);
}[HttpPut({id})]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Put(Guid id, [FromBody] Movie movie)
{var dbMovie _context.Movies.SingleOrDefault(x x.Id.Equals(id));if (dbMovie null){return NotFound();}dbMovie.Map(movie);_context.Movies.Update(dbMovie);_context.SaveChanges();return NoContent();
} 通过将 ValidationFilterAttribute 应用为服务过滤器我们无需在操作中使用验证代码。代码现在更干净、更具可读性。此外只要我们的模型类继承自 IEntity 接口此验证逻辑就可以重用。 为了确保操作过滤器的验证优先于 [ApiController] 属性的默认验证行为我们需要禁止默认验证。在 Startup 类或 .NET 6 的 Program 类中添加以下配置 services.ConfigureApiBehaviorOptions(options
{ options.SuppressModelStateInvalidFilter true ;
}); 此配置可确保返回操作过滤器的验证结果例如UnprocessableEntity而不是返回验证错误的默认BadRequest结果。 通过这些更改我们的验证过滤器已准备好进行测试。 动作过滤器中的依赖注入 为了消除通过 ID 从数据库中获取电影并在 GetById、DELETE 和 PUT 操作中检查其是否存在的代码重复我们可以创建一个新的操作过滤器来执行此任务。我们还将使用依赖注入将 MovieContext 注入到动作过滤器中。
让我们在 ActionFilters 文件夹中创建一个名为 ValidateEntityExistsAttributeT 的新操作过滤器类
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; namespace ActionFilters.ActionFilters
{ public class ValidateEntityExistsAttributeT : IActionFilter where T : class, IEntity { private readonly MovieContext _context; public ValidateEntityExistsAttribute(MovieContext context) { _context context; } public void OnActionExecuting(ActionExecutingContext context) { Guid id Guid.Empty; if (context.ActionArguments.ContainsKey(id)) { id (Guid)context.ActionArguments[id]; } else { context.Result new BadRequestObjectResult(Bad id parameter); return; } var entity _context.SetT().SingleOrDefault(x x.Id.Equals(id)); if (entity null) { context.Result new NotFoundResult(); } else { context.HttpContext.Items.Add(entity, entity); } } public void OnActionExecuted(ActionExecutedContext context) { } }
} 在ValidateEntityExistsAttributeT类中我们使用依赖注入通过构造函数注入MovieContext 。该类是通用的以便它可以重用于我们项目中的任何模型。在OnActionExecuting方法中我们从操作上下文中获取 ID 参数并检查该实体是否存在于数据库中。如果找到实体我们会将其存储在HttpContext .Items 集合中以便稍后在操作方法中使用。 接下来让我们在ConfigureServices方法中注册操作过滤器 services.AddScopedValidateEntityExistsAttributeMovie(); 对于 .NET 6我们在 Program 类中使用构建器变量
builder.Services.AddScopedValidateEntityExistsAttributeMovie();最后修改我们的操作以将 ValidateEntityExistsAttribute 应用为服务过滤器
[HttpGet({id}, Name MovieById)] [ServiceFilter(typeof(ValidateEntityExistsAttributeMovie))]
public IActionResult Get(Guid id)
{ var dbMovie HttpContext.Items[entity] as Movie; return Ok(dbMovie);
} [HttpPut({id})]
[ServiceFilter(typeof(ValidationFilterAttribute))]
[ServiceFilter(typeof(ValidateEntityExistsAttributeMovie))]
public IActionResult Put(Guid id, [FromBody]Movie movie)
{ var dbMovie HttpContext.Items[entity] as Movie; dbMovie.Map(movie); _context.Movies.Update(dbMovie); _context.SaveChanges(); return NoContent();
} [HttpDelete({id})]
[ServiceFilter(typeof(ValidateEntityExistsAttributeMovie))]
public IActionResult Delete(Guid id)
{ var dbMovie HttpContext.Items[entity] as Movie; _context.Movies.Remove(dbMovie); _context.SaveChanges(); return NoContent();
} 通过应用ValidateEntityExistsAttributeMovie作为服务过滤器我们确保从数据库中获取电影实体并通过HttpContext.Items在操作方法中可用。这消除了在获取实体并检查其存在时重复代码的需要。 通过这些更改我们的操作更加清晰、更具可读性并且通过 ID 获取实体的代码现在可以重用。