Search code examples
c#.net-coreasp.net-web-api-routing.net-core-2.2

Core API Controller to catch all unknown routes


I have a Core 2.2 API with a bunch of existing controllers. What i am now trying to do is add a new controller that acts similar to a catchall route, but only for that controller (and doesn't disturb the routes of the existing controllers).

In my existing controller i am defining the routes as controller attributes

[Route("api/[controller]")]
[ApiController]
public class SandboxController : ControllerBase
{
    [HttpGet("Hello")]
    public IEnumerable<string> Hello()
    {
        return new string[] { "Hello World", TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time")).ToString()};
    }
}

For this new "catchall" controller i need it to be able to catch any Get, Post, Put, Delete that is routed to it. For example this controllers route is ../api/catchall. If someone where to do a post to ../api/catchall/some/random/unknown/route i'm trying to catch this and route it to ../api/catchall/post.

So far i have be utterly unsuccessful. This is what i got so far:

In my Startup.cs

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseAuthentication();

...

    app.UseMvc(routes =>
    {
        routes.MapRoute("default", "{controller=Sandbox}/{action=Hello}/{id?}");

        routes.MapRoute(
            name: "catchall",
            template: "{controller}/{*.}", 
            defaults: new { controller = "catchall", action = "post" });
    });

And the catchall controller:

[Route("api/[controller]")]
[ApiController]
public class CatchallController : ControllerBase
{
    [HttpPost("post", Order = int.MaxValue)]
    public IActionResult Post([FromBody] string value)
    {
        return Content("{ \"name\":\"John Doe\", \"age\":31, \"city\":\"New York\" }", "application/json");
    }
}

Any ideas on how i can get this to work?


Solution

  • Catch all routes are specified with the * or ** syntax. Place [Route("{**catchall}")] on the action that you want to be the catch all action. This will make a catch all route for all routes prefixed with the prefix specified in the Controller route attribute.

    [Route("api/[controller]")]
    [ApiController]
    public class CatchallController : ControllerBase
    {
        [Route("{**catchAll}")]
        [HttpPost("post", Order = int.MaxValue)]
        public IActionResult Post([FromBody] string value, string catchAll)
        {
            return Content("{ \"name\":\"John Doe\", \"age\":31, \"city\":\"New York\" }", "application/json");
        }
    }
    

    In the example above, this will catch api/catchall/anything/following/it and set the string catchAll to anything/following/it

    If you want to set a site-wide catch all route you can use an absolute url

    [Route("/{**catchAll}")]
    public IActionResult CatchAll(string catchAll)
    {
    
    }
    

    This will catch any route that does not match any other specified route.