宿遷做網(wǎng)站短視頻入口seo
????????過(guò)濾器是 ASP.NET Core 中的特殊組件,允許我們?cè)谡?qǐng)求管道的特定階段控制請(qǐng)求的執(zhí)行。這些過(guò)濾器在中間件執(zhí)行后以及 MVC 中間件匹配路由并調(diào)用特定操作時(shí)發(fā)揮作用。
????????簡(jiǎn)而言之,過(guò)濾器提供了一種在操作級(jí)別自定義應(yīng)用程序行為的方法。它們就像檢查點(diǎn),允許我們執(zhí)行特定任務(wù),例如異常處理、緩存或添加自定義響應(yīng)標(biāo)頭。
????????當(dāng)請(qǐng)求到達(dá)某個(gè)操作時(shí),過(guò)濾器可以收集有關(guān)已選擇哪個(gè)操作以及關(guān)聯(lián)的路由數(shù)據(jù)的信息。此信息可用于做出決策并執(zhí)行特定于該特定操作或控制器的操作。
????????通過(guò)使用過(guò)濾器,我們?cè)谔幚碚?qǐng)求時(shí)擁有更多的控制權(quán)和靈活性。我們可以根據(jù)我們的要求實(shí)現(xiàn)自定義邏輯并將特定行為應(yīng)用于不同的操作或控制器。這有助于在 ASP.NET Core 應(yīng)用程序中實(shí)現(xiàn)更加定制和高效的請(qǐng)求處理流程。
過(guò)濾器的類型
????????ASP.NET Core 提供了一組預(yù)定義的過(guò)濾器類型,允許我們?cè)诓僮骰蚩刂破鞯纳舷挛闹锌刂普?qǐng)求的執(zhí)行。這些過(guò)濾器有不同的用途,可以根據(jù)我們的要求進(jìn)行定制。
以下是 ASP.NET Core 中預(yù)定義過(guò)濾器的主要類型:
-
AuthorizationFilter:此過(guò)濾器在路由執(zhí)行開(kāi)始時(shí)執(zhí)行。它確定是否允許用戶訪問(wèn)該路由或執(zhí)行請(qǐng)求的操作。
-
ResourceFilter:該過(guò)濾器在授權(quán)后運(yùn)行,可用于繞過(guò)剩余管道的執(zhí)行。如果預(yù)處理的響應(yīng)可用,我們想要發(fā)送緩存的響應(yīng),從而跳過(guò)管道的其余部分,那么它非常有用。
-
ActionFilter:動(dòng)作過(guò)濾器在動(dòng)作執(zhí)行之前和之后運(yùn)行。它提供了一種用自定義邏輯包圍操作或在執(zhí)行操作之前和之后執(zhí)行其他任務(wù)的方法。
-
ResultFilter:此過(guò)濾器在操作生成結(jié)果之前和之后執(zhí)行。它允許我們用附加行為包圍結(jié)果執(zhí)行或根據(jù)結(jié)果執(zhí)行某些操作。
-
ExceptionFilter:每當(dāng)操作或控制器中拋出未捕獲的異常時(shí),異常過(guò)濾器就會(huì)運(yùn)行。它提供了一種處理異常并設(shè)計(jì)特定于操作或控制器的自定義錯(cuò)誤響應(yīng)的方法。
-
ServiceFilter:此過(guò)濾器運(yùn)行另一個(gè)過(guò)濾器,其類型在其自身內(nèi)傳遞。當(dāng)傳遞的過(guò)濾器類型注冊(cè)為服務(wù)時(shí)使用它。它解決通過(guò)依賴項(xiàng)注入 (DI) 傳遞給過(guò)濾器類型的任何依賴項(xiàng)。
-
TypeFilter:與服務(wù)過(guò)濾器類似,類型過(guò)濾器也運(yùn)行未注冊(cè)為服務(wù)的過(guò)濾器類型。它允許我們應(yīng)用自定義過(guò)濾器而無(wú)需注冊(cè)。
????????這些預(yù)定義的過(guò)濾器類型使我們能夠靈活地控制請(qǐng)求執(zhí)行過(guò)程,并在 ASP.NET Core 應(yīng)用程序的管道的不同階段添加特定行為。
過(guò)濾器范圍?
????????ASP.NET Core 中過(guò)濾器的范圍取決于它們附加到 MVC 管道的方式。這使我們能夠控制過(guò)濾器執(zhí)行的時(shí)間和地點(diǎn)。過(guò)濾器的主要范圍分為三個(gè):
-
特定于操作的范圍:過(guò)濾器可以應(yīng)用于控制器內(nèi)的特定操作方法。通過(guò)使用過(guò)濾器屬性修飾操作,過(guò)濾器將僅在選擇該特定操作來(lái)處理傳入請(qǐng)求時(shí)執(zhí)行。
-
特定于控制器的范圍:過(guò)濾器也可以應(yīng)用于整個(gè)控制器類。當(dāng)過(guò)濾器應(yīng)用于控制器級(jí)別時(shí),它將針對(duì)該控制器內(nèi)的所有操作執(zhí)行。
-
全局范圍:全局過(guò)濾器應(yīng)用于路由中間件匹配和拾取的每條路由。這些過(guò)濾器被注冊(cè)為全局過(guò)濾器,并且無(wú)論特定操作或控制器如何都會(huì)被執(zhí)行。但是,如果沒(méi)有為請(qǐng)求選擇端點(diǎn),則不會(huì)執(zhí)行任何過(guò)濾器。
????????要將過(guò)濾器注冊(cè)為全局過(guò)濾器,可以將其添加到 Startup 類中的 ConfigureServices() 方法內(nèi)的過(guò)濾器數(shù)組中。這是一個(gè)例子:
services.AddControllers(options => {
? ? options.Filters.Add(typeof(ConsoleGlobalActionFilter));
});
????????在此示例中,使用 Add(typeof(...)) 方法將 ConsoleGlobalActionFilter 注冊(cè)為全局篩選器。這確保過(guò)濾器將應(yīng)用于路由中間件匹配和處理的每個(gè)路由。
????????通過(guò)控制過(guò)濾器的范圍,我們可以精確地確定過(guò)濾器將在 ASP.NET Core 應(yīng)用程序中應(yīng)用的時(shí)間和位置,從而允許我們添加特定的行為并有效地控制請(qǐng)求執(zhí)行過(guò)程。
如何創(chuàng)建簡(jiǎn)單的過(guò)濾器
有兩種方法可以創(chuàng)建我們之前討論的類型的過(guò)濾器。我們以創(chuàng)建ActionFilter為例:
-
實(shí)現(xiàn)接口:要?jiǎng)?chuàng)建根據(jù)操作執(zhí)行進(jìn)行操作的自定義操作過(guò)濾器,我們可以創(chuàng)建一個(gè)實(shí)現(xiàn) IActionFilter 或 IAsyncActionFilter 接口的類。這些接口提供了我們可以重寫以添加自定義邏輯的方法。通過(guò)實(shí)現(xiàn)這些接口,我們可以完全控制操作過(guò)濾器的行為。
-
擴(kuò)展屬性類:或者,我們可以創(chuàng)建一個(gè)擴(kuò)展 ActionFilterAttribute 類的自定義操作過(guò)濾器類。ActionFilterAttribute 類已經(jīng)實(shí)現(xiàn)了 IActionFilter 和 IAsyncActionFilter 接口。通過(guò)擴(kuò)展此類,我們可以重寫我們感興趣的方法并添加自定義邏輯。這種方法提供了一種更方便的方法來(lái)創(chuàng)建操作過(guò)濾器,因?yàn)槲覀兛梢灾苯訑U(kuò)展基類并專注于實(shí)現(xiàn)必要的方法。
????????同樣,對(duì)于提到的其他類型的過(guò)濾器(如授權(quán)過(guò)濾器、資源過(guò)濾器、結(jié)果過(guò)濾器、異常過(guò)濾器),也有相應(yīng)的可以實(shí)現(xiàn)的接口或可以擴(kuò)展的屬性類。
以下是上述過(guò)濾器的接口和屬性類:
-
IAuthorizationFilter:可以通過(guò)AuthorizationFilterAttribute實(shí)現(xiàn)或擴(kuò)展。
-
IResourceFilter:可以通過(guò)ResourceFilterAttribute實(shí)現(xiàn)或擴(kuò)展。
-
IActionFilter:可以通過(guò)ActionFilterAttribute實(shí)現(xiàn)或擴(kuò)展。
-
IResultFilter:可以通過(guò)ResultFilterAttribute實(shí)現(xiàn)或擴(kuò)展。
-
IExceptionFilter:可以通過(guò)ExceptionFilterAttribute實(shí)現(xiàn)或擴(kuò)展。
????????其中一些過(guò)濾器附帶有實(shí)現(xiàn)相應(yīng)接口的屬性類,使我們能夠靈活地重寫適合我們需要的特定方法。以下是一些過(guò)濾器的屬性類:
-
ActionFilterAttribute:該屬性類實(shí)現(xiàn) IActionFilter 和 IAsyncActionFilter 接口。通過(guò)擴(kuò)展這個(gè)類,我們可以直接繼承這些接口的實(shí)現(xiàn)并重寫我們感興趣的方法。這使得我們可以根據(jù)我們的需求自定義動(dòng)作過(guò)濾器的行為。
-
ExceptionFilterAttribute:該屬性類實(shí)現(xiàn) IExceptionFilter 接口。通過(guò)擴(kuò)展此類并重寫其方法,我們可以處理和自定義對(duì)操作或控制器中發(fā)生的異常的處理。這使我們能夠控制異常的處理方式,并允許我們提供自定義錯(cuò)誤響應(yīng)。
-
ResultFilterAttribute:該屬性類實(shí)現(xiàn) IResultFilter 接口。通過(guò)擴(kuò)展此類并重寫其方法,我們可以在將操作生成的結(jié)果發(fā)送回客戶端之前對(duì)其進(jìn)行修改。這允許我們向結(jié)果添加額外的處理或轉(zhuǎn)換。
-
FormatFilterAttribute:此屬性類用于指定操作或控制器支持的響應(yīng)格式。它有助于內(nèi)容協(xié)商并根據(jù)客戶的偏好選擇適當(dāng)?shù)捻憫?yīng)格式。
-
ServiceFilterAttribute:此屬性類用于應(yīng)用注冊(cè)為服務(wù)的過(guò)濾器。它允許我們使用依賴項(xiàng)注入來(lái)解決過(guò)濾器所需的任何依賴項(xiàng)。當(dāng)過(guò)濾器需要額外的服務(wù)或依賴項(xiàng)才能正常運(yùn)行時(shí),這會(huì)很有幫助。
-
TypeFilterAttribute:該屬性類與ServiceFilterAttribute類似,但它允許我們應(yīng)用未注冊(cè)為服務(wù)的過(guò)濾器。我們可以直接指定過(guò)濾器類型及其依賴項(xiàng)。
????????例如,ActionFilterAttribute類已經(jīng)為我們實(shí)現(xiàn)了IActionFilter和IAsyncActionFilter接口,因此我們可以直接擴(kuò)展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 = "I'm Alive" }); }
}
????????在此示例中,我們通過(guò)擴(kuò)展ActionFilterAttribute創(chuàng)建了自定義AddResponseHeaderFilter類。此過(guò)濾器在執(zhí)行操作之前和之后向 HTTP 響應(yīng)添加自定義響應(yīng)標(biāo)頭。它重寫OnActionExecutionAsync方法,該方法允許我們自定義操作執(zhí)行周圍的行為。
????????然后,我們使用 [ AddResponseHeaderFilter ] 屬性將AddResponseHeaderFilter過(guò)濾器應(yīng)用到HomeController類的Index操作。這可確保針對(duì)該特定操作執(zhí)行過(guò)濾器。
????????調(diào)用該操作時(shí),過(guò)濾器使用Response.Headers.Add方法將自定義標(biāo)頭添加到響應(yīng)中。然后它調(diào)用 next.Invoke() 方法來(lái)繼續(xù)執(zhí)行操作。執(zhí)行操作后,它會(huì)訪問(wèn)響應(yīng)結(jié)果并使用JsonConvert.SerializeObject 將其寫入控制臺(tái)。
????????通過(guò)利用ActionFilterAttribute并創(chuàng)建自定義過(guò)濾器類,我們可以擴(kuò)展和自定義 ASP.NET Core 請(qǐng)求管道的行為,以添加其他功能并根據(jù)我們的要求修改響應(yīng)。
動(dòng)作過(guò)濾器的實(shí)現(xiàn)
示例 1:實(shí)現(xiàn) IActionFilter 的同步操作過(guò)濾器?
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}}
}
????????在此示例中,我們創(chuàng)建一個(gè)名為 ActionFilterExample 的類,該類實(shí)現(xiàn) IActionFilter 接口。該接口要求我們實(shí)現(xiàn)兩個(gè)方法:OnActionExecuting 和 OnActionExecuted。OnActionExecuting 方法包含將在操作方法之前執(zhí)行的代碼,而 OnActionExecuted 方法包含將在操作方法之后執(zhí)行的代碼。?
示例 2:實(shí)現(xiàn) IAsyncActionFilter 的異步操作過(guò)濾器?
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}}
}
????????在此示例中,我們創(chuàng)建一個(gè)名為 AsyncActionFilterExample 的類,該類實(shí)現(xiàn) IAsyncActionFilter 接口。該接口要求我們實(shí)現(xiàn) OnActionExecutionAsync 方法,該方法是動(dòng)作過(guò)濾器的異步版本。該方法接收一個(gè) ActionExecutingContext 和一個(gè) ActionExecutionDelegate。ActionExecutionDelegate 代表管道中的下一個(gè)操作,我們可以等待它執(zhí)行操作方法。通過(guò)這樣做,我們可以在操作方法執(zhí)行之前和之后運(yùn)行代碼。
????????通過(guò)創(chuàng)建實(shí)現(xiàn) IActionFilter 或 IAsyncActionFilter 的類,我們可以定義要在 ASP.NET Core 中的操作方法之前和之后執(zhí)行的自定義邏輯。這些操作過(guò)濾器提供了一種向請(qǐng)求管道添加附加行為并根據(jù)需要修改請(qǐng)求或響應(yīng)的方法。
?操作過(guò)濾器的范圍
可以在不同范圍級(jí)別添加操作過(guò)濾器:全局、操作和控制器。
1. 全局范圍
要全局使用操作過(guò)濾器,您可以在ConfigureServices方法的AddControllers()方法中注冊(cè)它:
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());
});
通過(guò)將過(guò)濾器添加到全局范圍,它將應(yīng)用于整個(gè)應(yīng)用程序中的所有操作方法。
2. 操作和控制器范圍
如果要在操作或控制器級(jí)別使用操作過(guò)濾器,則需要在ConfigureServices方法中將其注冊(cè)為IoC容器中的服務(wù):
services.AddScoped<ActionFilterExample>();
services.AddScoped<ControllerFilterExample>();
在 .NET 6 及更高版本中:?
builder.Services.AddScoped<ActionFilterExample>();
builder.Services.AddScoped<ControllerFilterExample>();
????????通過(guò)將操作過(guò)濾器注冊(cè)為服務(wù),您可以通過(guò)使用相應(yīng)的過(guò)濾器屬性裝飾它們,有選擇地將其應(yīng)用到特定的操作方法或控制器。
namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample))][Route("api/[controller]")][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample))]public IEnumerable<string> Get(){return new string[] { "example", "data" };}}
}
????????通過(guò)使用[ServiceFilter]屬性并指定過(guò)濾器類型,您可以將注冊(cè)的過(guò)濾器(ControllerFilterExample 和 ActionFilterExample)分別與控制器和操作方法關(guān)聯(lián)起來(lái)。
調(diào)用順序
我們的過(guò)濾器的執(zhí)行順序如下:
我們可以通過(guò)向ServiceFilter屬性添加一個(gè)名為Order 的附加屬性來(lái)更改多個(gè)過(guò)濾器的調(diào)用順序:?
namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample), Order=2)][Route("api/[controller]")][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample), Order=1)]public IEnumerable<string> Get(){return new string[] { "example", "data" };}}
}
????????在本例中,ControllerFilterExample將在ActionFilterExample之前執(zhí)行,因?yàn)樗鼈兊捻樞蛑捣謩e為 2 和 1。通過(guò)為過(guò)濾器分配不同的順序值,您可以控制它們的執(zhí)行順序。
[HttpGet]
[ServiceFilter(typeof(ActionFilterExample), Order=2)]
[ServiceFilter(typeof(ActionFilterExample2), Order=1)]
public IEnumerable<string> Get()
{ return new string[] { "example", "data ” };
}
?????????在這種情況下,ActionFilterExample2將根據(jù)其順序值分別為 1 和 2,在ActionFilterExample之前執(zhí)行。
使用操作過(guò)濾器改進(jìn)代碼
????????為了使用操作過(guò)濾器改進(jìn)代碼,讓我們重點(diǎn)關(guān)注存儲(chǔ)庫(kù)中AppStart文件夾中起始項(xiàng)目的Controllers文件夾中的MoveController類。該控制器包含所有 CRUD 操作的實(shí)現(xiàn)。
盡管我們的操作已經(jīng)干凈且可讀,但由于全局異常處理,我們可以進(jìn)一步增強(qiáng)它們。
需要注意的一件重要事情是我們的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; }
}
?????????現(xiàn)在,讓我們關(guān)注POST和PUT操作的驗(yàn)證代碼。通過(guò)將適當(dāng)?shù)牟僮鬟^(guò)濾器應(yīng)用于POST和PUT操作,我們可以消除這些操作中顯式驗(yàn)證代碼的需要,使它們更加簡(jiǎn)潔和可維護(hù)。
使用操作過(guò)濾器進(jìn)行驗(yàn)證
????????為了改進(jìn)POST和PUT操作中的驗(yàn)證代碼,我們可以使用操作過(guò)濾器。通過(guò)將驗(yàn)證邏輯提取到自定義操作過(guò)濾器中,我們可以使代碼更具可重用性并保持操作更清晰。
????????首先,我們?cè)诮鉀Q方案資源管理器中創(chuàng)建一個(gè)名為“ActionFilters”的新文件夾。在該文件夾中,創(chuàng)建一個(gè)名為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方法來(lái)執(zhí)行驗(yàn)證邏輯。我們檢查操作參數(shù)是否為 IEntity 類型,如果為 null,則返回BadRequestObjectResult。此外,如果 ModelState 無(wú)效,我們將返回UnprocessableEntityObjectResult。
????????接下來(lái),讓我們?cè)贑onfigureServices方法中將此操作過(guò)濾器注冊(cè)為服務(wù):
public void ConfigureServices(IServiceCollection services)
{services.AddDbContext<MovieContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("sqlConString")));services.AddScoped<ValidationFilterAttribute>();services.AddControllers();
}
對(duì)于.NET 6,我們需要在 Program 類中使用構(gòu)建器變量:?
builder.Services.AddDbContext<MovieContext>(options => options.UseSqlServer(Configuration.GetConnectionString( "sqlConString" ))); builder.Services.AddScoped<ValidationFilterAttribute>(); builder.Services.AddControllers();
最后,從 POST 和 PUT 操作中刪除驗(yàn)證代碼,并將ValidationFilterAttribute作為服務(wù)應(yīng)用:
[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();
}
????????通過(guò)將 ValidationFilterAttribute 應(yīng)用為服務(wù)過(guò)濾器,我們無(wú)需在操作中使用驗(yàn)證代碼。代碼現(xiàn)在更干凈、更具可讀性。此外,只要我們的模型類繼承自 IEntity 接口,此驗(yàn)證邏輯就可以重用。
services.Configure<ApiBehaviorOptions>(options =>
{ options.SuppressModelStateInvalidFilter = true ;
});
?????????此配置可確保返回操作過(guò)濾器的驗(yàn)證結(jié)果(例如,UnprocessableEntity),而不是返回驗(yàn)證錯(cuò)誤的默認(rèn)BadRequest結(jié)果。
動(dòng)作過(guò)濾器中的依賴注入
????????為了消除通過(guò) ID 從數(shù)據(jù)庫(kù)中獲取電影并在 GetById、DELETE 和 PUT 操作中檢查其是否存在的代碼重復(fù),我們可以創(chuàng)建一個(gè)新的操作過(guò)濾器來(lái)執(zhí)行此任務(wù)。我們還將使用依賴注入將 MovieContext 注入到動(dòng)作過(guò)濾器中。
讓我們?cè)?ActionFilters 文件夾中創(chuàng)建一個(gè)名為 ValidateEntityExistsAttribute<T> 的新操作過(guò)濾器類:
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; namespace ActionFilters.ActionFilters
{ public class ValidateEntityExistsAttribute<T> : 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.Set<T>().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) { } }
}
????????在ValidateEntityExistsAttribute<T>類中,我們使用依賴注入通過(guò)構(gòu)造函數(shù)注入MovieContext 。該類是通用的,以便它可以重用于我們項(xiàng)目中的任何模型。在OnActionExecuting方法中,我們從操作上下文中獲取 ID 參數(shù)并檢查該實(shí)體是否存在于數(shù)據(jù)庫(kù)中。如果找到實(shí)體,我們會(huì)將其存儲(chǔ)在HttpContext .Items 集合中,以便稍后在操作方法中使用。
????????接下來(lái),讓我們?cè)?strong>ConfigureServices方法中注冊(cè)操作過(guò)濾器:
?services.AddScoped<ValidateEntityExistsAttribute<Movie>>();
????????對(duì)于 .NET 6,我們?cè)?Program 類中使用構(gòu)建器變量:?
builder.Services.AddScoped<ValidateEntityExistsAttribute<Movie>>();
最后,修改我們的操作以將 ValidateEntityExistsAttribute 應(yīng)用為服務(wù)過(guò)濾器:
[HttpGet("{id}", Name = "MovieById")] [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))]
public IActionResult Get(Guid id)
{ var dbMovie = HttpContext.Items["entity"] as Movie; return Ok(dbMovie);
} [HttpPut("{id}")]
[ServiceFilter(typeof(ValidationFilterAttribute))]
[ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))]
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(ValidateEntityExistsAttribute<Movie>))]
public IActionResult Delete(Guid id)
{ var dbMovie = HttpContext.Items["entity"] as Movie; _context.Movies.Remove(dbMovie); _context.SaveChanges(); return NoContent();
}
????????通過(guò)應(yīng)用ValidateEntityExistsAttribute<Movie>作為服務(wù)過(guò)濾器,我們確保從數(shù)據(jù)庫(kù)中獲取電影實(shí)體并通過(guò)HttpContext.Items在操作方法中可用。這消除了在獲取實(shí)體并檢查其存在時(shí)重復(fù)代碼的需要。
????????通過(guò)這些更改,我們的操作更加清晰、更具可讀性,并且通過(guò) ID 獲取實(shí)體的代碼現(xiàn)在可以重用。