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

No action was found on the controller ApiController


For my API, I want to be able to handle scenarios where the call is made using an incorrect URL (i.e. URL that does not match any controller or action.)

For this I have implemented routing so that it matches any route:

config.Routes.MapHttpRoute(
    name: "CatchAll",
    routeTemplate: "{*any}",
    defaults: new { controller = "Error", action = "Handler" }
);

And I have implemented the controller - action as follows:

[Route("api/v1/Handler")]
[ActionName("Handler")]
public HttpResponseMessage Get() {...}

When I give an incorrect URL, I get the message:

No action was found on the controller 'Error' that matches the name 'Handler'

Not sure where the mistake is.


Solution

  • Simple approach:

    You need to match the action name. So either rename Get to Handler in the ErrorController and remove attributes as they will conflict with conventions you mapped .

    public HttpResponseMessage Handler(string any) {...}
    

    or change action from Handler to Get in the Catch all MapHttpRoute

    config.Routes.MapHttpRoute(
                name: "CatchAll",
                routeTemplate: "{*any}",
                defaults: new { controller = "Error", action = "Get" }
                );
    

    Not so simple approach:

    Here is how I handled the same thing in my web api using attribute routing and inheritance.

    First I created a base api controller that will handler unknown actions.

    public abstract class WebApiControllerBase : ApiController {
    
        [Route("{*actionName}")]
        [AcceptVerbs("GET", "POST", "PUT", "DELETE")]
        [AllowAnonymous]
        [ApiExplorerSettings(IgnoreApi = true)]
        public virtual HttpResponseMessage HandleUnknownAction(string actionName) {
            var status = HttpStatusCode.NotFound;
            var message = "[Message placeholder]";
            var content = new { message = message, status = status};
            return Request.CreateResponse(status, content);
        }
    }
    

    My Api controllers all inherit from this base controller.

    [RoutePrefix("api/customers")]
    public class CustomersApiController : WebApiControllerBase {...}
    

    Given that your question included RouteAttribute you should already be using MapHttpAttributeRoutes in your web api config.

    But there's more. To get the framework to recognize the inherited attributes you need to override the DefaultDirectRouteProvider as outlined here

    and used in an answer here

    WebAPI controller inheritance and attribute routing

    public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider {
        protected override System.Collections.Generic.IReadOnlyList<IDirectRouteFactory>
            GetActionRouteFactories(System.Web.Http.Controllers.HttpActionDescriptor actionDescriptor) {
            // inherit route attributes decorated on base class controller's actions
            return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
        }
    }
    

    and apply that to the web api configuration.

    // Attribute routing. (with inheritance)
    config.MapHttpAttributeRoutes(new WebApiCustomDirectRouteProvider());
    

    Hope this helps