ASP .Net Core在运行时更改路由

时间:2018-10-18 13:02:29

标签: c# asp.net asp.net-core .net-core asp.net-core-2.0

我的问题:我有多个控制器类,我希望它们的路由根据外部配置文件中的某个值(可以称为ID)更改(可以更改)。该ID不是恒定的,而是在应用程序启动时生成的。

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

更新 然后我有Test2Controller,它与Test1Controller基本相同,但是返回不同的视图

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

所以可以说我的配置文件中有

Test1 : 123
Test2 : 456

因此,当我调用https://localhost:44391/api/projects/123/Index时,我想从Test1Controller获取索引页;当我调用https://localhost:44391/api/projects/456/Index时,我要从Test2Controller获取索引页

有什么办法可以做到这一点? 谢谢

2 个答案:

答案 0 :(得分:6)

如果该标识符是在启动时生成的,但是然后是恒定的,则只需在UseMvc()方法内调用Configure时即可生成动态路由映射:

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" });

    // …
});

答案 1 :(得分:4)

如果您想使用一种更灵活的方法,可以考虑使用自定义控制器约定,可以使用IControllerModelConvention接口来实现。使用此功能,您可以将配置对象传递到自定义约定中,并使用该约定来应用路由。有很多方法可以解决此问题,但这是一个示例实现:

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

在此示例中,我将IConfiguration的实例带入构造函数,该实例由以下appsettings.json填充:

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

我意识到您可能会在您的配置中使用其他功能,但是在本示例中使用此方法应该可以更简单地说明问题。

在为项目中的每个控制器调用的RoutingControllerModelConvention.Apply方法中,我们从IConfiguration实例中查找相应的值,在该实例中,我们使用controllerModel.ControllerName来获取例如Test1。在此示例中,这为我们提供了123的值。接下来,我们抓住第一个选择器(总是至少有一个选择器),最终将其路由模板设置为/api/projects/123/[action]

使用这种方法,您不需要将[Route]属性应用于控制器本身,也不需要在MapRoute中使用Startup。添加新控制器时,您需要做的就是创建控制器,并在此示例中向appsettings.json添加一个条目。

要使用此自定义约定,您需要在Startup.ConfigureServices中对其进行配置:

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

有关更多信息,此处记录了应用程序模型和约定:Work with the application model in ASP.NET Core


我很欣赏上面的实现不是完美的:您想检查null以及配置中找不到的控制器名称,等等。这至少应该用作让您开始采用非常灵活的方法。