忽略 ASP.NET Web API 中的控制器
Ignore controller in ASP.NET Web API
我们的团队维护着一个自托管 ASP.NET 网站 API。该项目使用属性路由,我们有几十个现有的控制器。可以说,API 通过主路径 /api/purpose1/...
公开,所有现有控制器都作为资源放置在下面。
现在我想介绍一个新的并行主路径,e。 G。 /api/purpose2/
。应该可以通过配置文件中的布尔变量独立激活两个主路径。
由于所有控制器都在一个程序集中,属性路由方法总是会找到它们并将它们添加到 purpose1
和 purpose2
。这与purpose1
和purpose2
的独立性相矛盾。所以我为 purpose1
使用了属性路由,为 purpose2
使用了基于约定的路由。这至少奏效了,但我对两种不同路由方法的混合感到不满意。
所以我的问题是:我可以使用属性路由禁用某些控制器 类 吗?
OnActionExecuting 示例:
V1控制器
[Route("api/[controller]")]
[ApiController]
public class SampleV1Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V1");
}
}
V2 控制器
[Route("api/[controller]")]
[ApiController]
public class SampleV2Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V2");
}
}
版本控制基础控制器
public abstract class VersioningAwareControllerBase: ControllerBase, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
if (!FeatureFlags.ShouldDeprecateV1 ||
!string.Equals(context.RouteData.Values["controller"].ToString(), "samplev1",
StringComparison.OrdinalIgnoreCase))
return;
context.Result = NotFound();
context.Canceled = true;
}
public void OnActionExecuting(ActionExecutingContext context) { }
}
很好,但是它依赖于 System.Web.Mvc
。在我们的例子中,这种依赖性以前不存在,我找到了一个不需要添加它的解决方案。
我通过以下方式扩展了 ApiControllerActionInvoker
:
internal class CustomHttpActionInvoker : ApiControllerActionInvoker
{
public CustomHttpActionInvoker(IConfigProvider configProvider)
{
ConfigProvider = configProvider;
InvokeActionFunc = base.InvokeActionAsync;
}
/// <summary>FOR AUTOMATED TESTS ONLY</summary>
internal CustomHttpActionInvoker(IConfigProvider configProvider,
Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> invokeActionFunc)
{
ConfigProvider = configProvider;
InvokeActionFunc = invokeActionFunc;
}
private IConfigProvider ConfigProvider { get; }
private Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> InvokeActionFunc { get; }
/// <inheritdoc />
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var isRelevantRequest = actionContext.ControllerContext.Controller is MyRelevantController;
if (isRelevantRequest && ConfigProvider.IsPurpose1)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
if (!isRelevantRequest && ConfigProvider.IsPurpose2)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
引入了 internal
构造函数以支持更简单的单元测试。
以下代码注册自定义 class:
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Services.Replace(typeof(IHttpActionInvoker), new CustomHttpActionInvoker(MyConfigProvider));
我们的团队维护着一个自托管 ASP.NET 网站 API。该项目使用属性路由,我们有几十个现有的控制器。可以说,API 通过主路径 /api/purpose1/...
公开,所有现有控制器都作为资源放置在下面。
现在我想介绍一个新的并行主路径,e。 G。 /api/purpose2/
。应该可以通过配置文件中的布尔变量独立激活两个主路径。
由于所有控制器都在一个程序集中,属性路由方法总是会找到它们并将它们添加到 purpose1
和 purpose2
。这与purpose1
和purpose2
的独立性相矛盾。所以我为 purpose1
使用了属性路由,为 purpose2
使用了基于约定的路由。这至少奏效了,但我对两种不同路由方法的混合感到不满意。
所以我的问题是:我可以使用属性路由禁用某些控制器 类 吗?
OnActionExecuting 示例:
V1控制器
[Route("api/[controller]")]
[ApiController]
public class SampleV1Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V1");
}
}
V2 控制器
[Route("api/[controller]")]
[ApiController]
public class SampleV2Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V2");
}
}
版本控制基础控制器
public abstract class VersioningAwareControllerBase: ControllerBase, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
if (!FeatureFlags.ShouldDeprecateV1 ||
!string.Equals(context.RouteData.Values["controller"].ToString(), "samplev1",
StringComparison.OrdinalIgnoreCase))
return;
context.Result = NotFound();
context.Canceled = true;
}
public void OnActionExecuting(ActionExecutingContext context) { }
}
System.Web.Mvc
。在我们的例子中,这种依赖性以前不存在,我找到了一个不需要添加它的解决方案。
我通过以下方式扩展了 ApiControllerActionInvoker
:
internal class CustomHttpActionInvoker : ApiControllerActionInvoker
{
public CustomHttpActionInvoker(IConfigProvider configProvider)
{
ConfigProvider = configProvider;
InvokeActionFunc = base.InvokeActionAsync;
}
/// <summary>FOR AUTOMATED TESTS ONLY</summary>
internal CustomHttpActionInvoker(IConfigProvider configProvider,
Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> invokeActionFunc)
{
ConfigProvider = configProvider;
InvokeActionFunc = invokeActionFunc;
}
private IConfigProvider ConfigProvider { get; }
private Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> InvokeActionFunc { get; }
/// <inheritdoc />
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var isRelevantRequest = actionContext.ControllerContext.Controller is MyRelevantController;
if (isRelevantRequest && ConfigProvider.IsPurpose1)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
if (!isRelevantRequest && ConfigProvider.IsPurpose2)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
引入了 internal
构造函数以支持更简单的单元测试。
以下代码注册自定义 class:
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Services.Replace(typeof(IHttpActionInvoker), new CustomHttpActionInvoker(MyConfigProvider));