Search code examples
asp.net-coreasp.net-web-apiasp.net-mvc-routing.net-6.0

How to use fallback routing in asp.net core?


I am using asp.net web-api with controllers. I want to do a user section where one can request the site's address with the username after it like example.com/username. The other, registered routes like about, support, etc. should have a higher priority, so if you enter example.com/about, the about page should go first and if no such about page exists, it checks whether a user with such name exists. I only have found a way for SPA fallback routing, however I do not use a SPA. Got it working manually in a middleware, however it is very complicated to change it.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

string[] internalRoutes = new string[] { "", "about", "support", "support/new-request", "login", "register" };

string[] userNames = new string[] { "username1", "username2", "username3" };

app.Use(async (context, next) =>
{
    string path = context.Request.Path.ToString();
    path = path.Remove(0, 1);

    path = path.EndsWith("/") ? path[0..^1] : path;

    foreach (string route in internalRoutes)
    {
        if (route == path)
        {
            await context.Response.WriteAsync($"Requested internal page '{path}'.");
            return;
        }
    }

    foreach (string userName in userNames)
    {
        if (userName == path)
        {
            await context.Response.WriteAsync($"Requested user profile '{path}'.");
            return;
        }
    }

    await context.Response.WriteAsync($"Requested unknown page '{path}'.");
    return;

    await next(context);
});

app.Run();

Solution

  • It's really straightforward with controllers and attribute routing. First, add controller support with app.MapControllers(); (before app.Run()).

    Then, declare your controller(s) with the appropriate routing. For simplicity, I added a single one that just returns simple strings.

    public class MyController : ControllerBase
    {
        [HttpGet("/about")]
        public IActionResult About()
        {
            return Ok("About");
        }
    
        [HttpGet("/support")]
        public IActionResult Support()
        {
            return Ok("Support");
        }
    
        [HttpGet("/support/new-request")]
        public IActionResult SupportNewRequest()
        {
            return Ok("New request support");
        }
    
        [HttpGet("/{username}")]
        public IActionResult About([FromRoute] string username)
        {
            return Ok($"Hello, {username}");
        }
    }
    

    The routing table will first check if there's an exact match (e.g. for /about or /support), and if not, if will try to find a route with matching parameters (e.g. /Métoule will match the /{username} route).