Search code examples
c#asp.netasp.net-web-apiasp.net-routing

Ignore controller in ASP.NET Web API


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?


Solution

  • 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));