Our team maintains a self-hosted ASP.NET Web API. The project uses attribute routing and we have dozens of existing controllers. Lets say, the API is exposed via the main path /api/purpose1/...
with all the existing controllers being placed as resources underneath.
Now I want to introduce a new parallel main path, e. g. /api/purpose2/
. It should be possible to activate both main paths independently of each other via a boolean variable in a config file.
Since all the controllers are within one assembly, the attribute routing approach always finds and adds them to both purpose1
and purpose2
. This contradicts the independency of purpose1
and purpose2
. So I used attribute routing for purpose1
and convention-based routing for purpose2
. That at least worked, but I'm not happy with the mixture of two different routing approaches.
So my question is: can I disable certain controller classes with attribute routing?
Peter Csala's answer is fine, however, it has a dependency to System.Web.Mvc
. In our case, this dependency wasn't there before and I found a solution that does not require adding it.
I've extended ApiControllerActionInvoker
the following way:
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));
}
}
The internal
constructor was introduced to support easier unit testing.
The following code registers the custom class:
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Services.Replace(typeof(IHttpActionInvoker), new CustomHttpActionInvoker(MyConfigProvider));