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

Dav*_*vid 2 c# asp.net .net-core asp.net-core asp.net-core-2.0

我的问题:我有多个控制器类,我希望他们的路由改变取决于外部配置文件中的某个值(让我们称之为ID)(可以更改).该ID不是常量,它是在应用程序启动时生成的.

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

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

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

所以我在配置文件中说:

Test1 : 123
Test2 : 456
Run Code Online (Sandbox Code Playgroud)

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

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

pok*_*oke 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" });

    // …
});
Run Code Online (Sandbox Code Playgroud)


Kir*_*kin 5

如果您想使用一种更灵活的方法,则可以考虑使用自定义控制器约定,可以使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,我将实例化IConfiguration到构造函数中,该实例由以下内容填充appsettings.json

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我意识到您很可能会配置中使用其他方法,但是在本示例中使用此方法应该有助于更简单地解释。

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

通过这种方法,你不必应用[Route]到控制器本身的属性,你不需要使用MapRouteStartup。添加新控制器时,您所需要做的就是创建控制器,并相应地向(在此示例中)添加一个条目appsettings.json

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

services.AddMvc(options =>
{
    options.Conventions.Add(new RoutingControllerModelConvention(Configuration));
});
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请在此处记录应用程序模型和约定:在ASP.NET Core中使用应用程序模型


我很欣赏上面的实现不是完美的:您需要检查nulls和配置中找不到的控制器名称,等等。这至少应该可以让您开始使用一种相当不错的方法灵活。

  • 非常不错的解决方案:)我尝试了它,它的工作原理就像一个魅力,这也许是更好的方法,因为现在我有3个控制器,但是将来肯定会有更多。谢谢 (2认同)