David
David

Reputation: 122

ASP .Net Core changing route on runtime

My problem: I have multiple controller classes and I want that their route change depending on some value (let's call it ID) that is in external config file(can change). That ID is not constant it is generated on application start up.

[Route("api/projects/" + idForTest1FromConfigFile]
public class Test1Controller : Controller 
{
    public IActionResult Index()
    {
        return View();
    }
}

UPDATE Then I Have Test2Controller which is basically same as Test1Controller but returns different views

[Route("api/projects/" + idForTest2FromConfigFile]
public class Test2Controller : Controller 
{
    public IActionResult Index()
    {
        return View();
    }
}

So lets say in my config file I have:

Test1 : 123
Test2 : 456

So when I call https://localhost:44391/api/projects/123/Index I want to get the index page from Test1Controller and when I call https://localhost:44391/api/projects/456/Index I want to get Index page from Test2Controller

is there any way that this could be done? Thanks

Upvotes: 3

Views: 5705

Answers (2)

Kirk Larkin
Kirk Larkin

Reputation: 93043

If you want to use an approach that's a bit more flexible, you could consider using a custom controller convention, which can be implemented using the IControllerModelConvention interface. Using this, you could pass a configuration object into a custom convention and apply the routes using that. There are number of ways to tackle this, but here's a sample implementation:

public class RoutingControllerModelConvention : IControllerModelConvention
{
    private readonly IConfiguration _configuration;

    public RoutingControllerModelConvention(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void Apply(ControllerModel controllerModel)
    {
        const string RouteTemplate = "/api/projects/<id>/[action]";

        var routeId = _configuration["RouteIds:" + controllerModel.ControllerName];
        var firstSelector = controllerModel.Selectors[0];

        if (firstSelector.AttributeRouteModel == null)
            firstSelector.AttributeRouteModel = new AttributeRouteModel();

        firstSelector.AttributeRouteModel.Template = RouteTemplate.Replace("<id>", routeId);
    }
}

In this example, I'm taking an instance of IConfiguration into the constructor, which is populated from the following appsettings.json:

{
    "RouteIDs": {
        "Test1": 123,
        "Test2": 234
    }
}

I realise you may well be using something else for your configuration, but using this approach in this example should help explain things more simply.

In the RoutingControllerModelConvention.Apply method, which gets called for each controller in your project, we look up the corresponding value from our IConfiguration instance, where we use controllerModel.ControllerName to get e.g. Test1. In this example, this gives us a value of 123. Next, we grab the first selector (there's always at least one) and, ultimately, set its route template to be /api/projects/123/[action].

With this approach, you don't need to apply a [Route] attribute to the controller itself and you don't need to use MapRoute in Startup. All you'd need to do when adding new controllers is create the controller and add an entry to (in this example) appsettings.json, accordingly.

In order to use this custom convention, you'd need to configure it in Startup.ConfigureServices:

services.AddMvc(options =>
{
    options.Conventions.Add(new RoutingControllerModelConvention(Configuration));
});

For more information, the application model and conventions are documented here: Work with the application model in ASP.NET Core.


I appreciate that the implementation above isn't perfect: You'd want to check for nulls and for controller names that aren't found in the configuration, etc. This should at least serve as something to get you started on an approach that's quite flexible.

Upvotes: 7

poke
poke

Reputation: 387547

If that identifier is generated at startup but then it’s constant, you can just generate a dynamic route mapping when you call UseMvc() inside of the Configure method:

var id1 = GetIdFromSomewhere();
var id2 = GetIdFromSomewhere();
app.UseMvc(routes =>
{
    // Test1Controller
    routes.MapRoute("test-route-1",
        "/api/projects/" + id1 + "/{action}",
        new { controller = "Test1", action = "Index" });

    // Test2Controller
    routes.MapRoute("test-route-2",
        "/api/projects/" + id2 + "/{action}",
        new { controller = "Test2", action = "Index" });

    // …
});

Upvotes: 7

Related Questions