网站建设电话营销话术,线上问诊网站建设,禹城市网站建设,荆州做网站哪家好基于 filter 实现条件路由Intro在我们的项目有几个测试用的接口#xff0c;有的接口我们往往不想在生产环境上使用#xff0c;于是会在代码里判断当前环境是不是生产环境#xff0c;如果不是生产环境才允许执行#xff0c;否则就返回一个错误#xff0c;这样的接口多了之后…基于 filter 实现条件路由Intro在我们的项目有几个测试用的接口有的接口我们往往不想在生产环境上使用于是会在代码里判断当前环境是不是生产环境如果不是生产环境才允许执行否则就返回一个错误这样的接口多了之后就会发现很多重复的代码我们此时就可以使用一个 filter 来实现 API 接口的检查如果是生产环境就不执行 API 接口的逻辑Filter V1MVC filter 有几种类型AuthorizationFilter、ResourceFilter、ActionFilter、ResultFilter、ExceptionFilter, 首先我们要选择合适的类型最合适的莫过于 ResourceFilter 和 ActionFilter可能大多小伙伴对于 ActionFilter 更为熟悉一些但是我觉得这种场景下 ResourceFilter 更好一些从 MVC filter 的执行流程上来说会依次执行 AuthorizationFilter、ResourceFilter、ActionFilter而我们的条件并非一种授权所以个人感觉 ResourceFilter 更合适一些我们可以使用 IAsyncResourceFilter 来实现实现代码如下[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NonProductionOnlyFilter : Attribute, IAsyncResourceFilter
{public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next){var environment context.HttpContext.RequestServices.GetRequiredServiceIWebHostEnvironment();if (environment.IsProduction()){context.Result new NotFoundResult();}else{await next();}}
}Filter V2为了更加的通用我们可以把检查的逻辑和返回值逻辑封装成一个委托[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ConditionalFilter : Attribute, IAsyncResourceFilter
{public FuncHttpContext, bool ConditionFunc { get; init; } _ true;public FuncHttpContext, IActionResult ResultFactory { get; init; } _ new NotFoundResult();public virtual async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next){var condition ConditionFunc.Invoke(context.HttpContext);if (condition){await next();}else{var result ResultFactory.Invoke(context.HttpContext);context.Result result;}}
}再在这个 ConditionalFilter 的基础上实现上面的逻辑[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited true, AllowMultiple false)]
public sealed class NonProductionEnvironmentFilter : ConditionalFilter
{public NonProductionEnvironmentFilter(){ConditionFunc c c.RequestServices.GetRequiredServiceIWebHostEnvironment().IsProduction() false;}
}看起来是不是简单了很多对于别的情况也比较容易扩展比如我们实现一个指定环境生效的条件[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited true, AllowMultiple false)]
public sealed class EnvironmentFilter : ConditionalFilter
{public EnvironmentFilter(params string[] environmentNames){Guard.NotNull(environmentNames);var allowedEnvironments environmentNames.ToHashSet(StringComparer.OrdinalIgnoreCase);ConditionFunc c {var env c.RequestServices.GetRequiredServiceIWebHostEnvironment().EnvironmentName;return allowedEnvironments.Contains(env);};}
}Filter V3在前面的文章中我们有提到在 .NET 7 中针对于 Minimal API引入了 EndpointFilter我们也可以为我们的 ConditionalFilter 添加 EndpointFilter 的支持public class ConditionalFilter : Attribute, IAsyncResourceFilter
#if NET7_0_OR_GREATER, IEndpointFilter
#endif{public FuncHttpContext, bool ConditionFunc { get; init; } _ true;public FuncHttpContext, object ResultFactory { get; init; } _
#if NET7_0_OR_GREATERResults.NotFound()
#elsenew NotFoundResult()
#endif;public virtual async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next){var condition ConditionFunc.Invoke(context.HttpContext);if (condition){await next();}else{var result ResultFactory.Invoke(context.HttpContext);context.Result result switch{IActionResult actionResult actionResult,IResult httpResult new HttpResultActionResultAdapter(httpResult),_ new OkObjectResult(result)};}}
#if NET7_0_OR_GREATERpublic virtual async ValueTaskobject InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next){var result ConditionFunc.Invoke(context.HttpContext);if (result){return await next(context);}return ResultFactory.Invoke(context.HttpContext);}
#endif
}这里有个需要注意的地方就是 EndpointFilter 的返回和 Resource filter 的返回值不同返回的类型不是 IActionResult 而且不能正确的处理 IActionResult 类型针对 IResult 会有处理所以我们针对 .NET 7 及以上返回的是 IResult 类型在 ResourceFilter 中处理逻辑中针对 IResult 再转成了 IActionResult, 也就是上面的 HttpResultActionResultAdapter实现也很简单实现如下internal sealed class HttpResultActionResultAdapter : IActionResult
{private readonly IResult _result;public HttpResultActionResultAdapter(IResult result){_result result;}public Task ExecuteResultAsync(ActionContext context){return _result.ExecuteAsync(context.HttpContext);}
}Demo测试代码分为 Minimal API 的 endpoint API 和 MVC controller,示例代码如下var envGroup app.MapGroup(/env-test);
envGroup.Map(/dev, () env-test).AddEndpointFilter(new EnvironmentFilter(Environments.Development));
envGroup.Map(/prod, () env-test).AddEndpointFilter(new EnvironmentFilter(Environments.Production));[HttpGet(EnvironmentFilterTest/Dev)]
[EnvironmentFilter(Development)]
//[EnvironmentFilter(Production)]
public IActionResult EnvironmentFilterDevTest()
{return Ok(new { Title .NET is amazing! });
}[HttpGet(EnvironmentFilterTest/Prod)]
[EnvironmentFilter(Production)]
public IActionResult EnvironmentFilterProdTest()
{return Ok(new { Title .NET is amazing! });
}访问我们的 API 来测试一下返回结果Minimal APIcontroller我们启动的时候默认的环境是 Development所以 Production 返回的都是 404而 Development 相关的 API 则是正常返回了~Referenceshttps://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filtershttps://github.com/WeihanLi/WeihanLi.Web.Extensionshttps://github.com/WeihanLi/WeihanLi.Web.Extensions/tree/dev/samples/WeihanLi.Web.Extensions.Samples