【问题标题】:ASP .Net Core changing route on runtimeASP .Net Core 在运行时更改路由
【发布时间】:2019-03-23 07:15:01
【问题描述】:

我的问题:我有多个控制器类,我希望它们的路由根据外部配置文件中的某个值(我们称之为 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 获取索引页

有什么办法可以做到吗? 谢谢

【问题讨论】:

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


    【解决方案1】:

    如果该标识符是在启动时生成的,但那么它是恒定的,您可以在Configure 方法内调用UseMvc() 时生成动态路由映射:

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

    【讨论】:

    • 我试过这种方式..它在本地主机中完美运行,但在托管站点中却不行。我们是否需要额外做任何事情来访问这些路由。
    【解决方案2】:

    如果您想使用更灵活的方法,可以考虑使用自定义控制器约定,可以使用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] 属性应用于控制器本身,也无需在Startup 中使用MapRoute。添加新控制器时,您需要做的就是创建控制器并相应地向(在此示例中)appsettings.json 添加一个条目。

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

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

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


    我很欣赏上面的实现并不完美:您需要检查 nulls 和在配置中找不到的控制器名称等。这至少应该可以帮助您开始采用一种非常灵活的方法。

    【讨论】:

    • 非常好的解决方案 :) 我试过了,它就像一个魅力,这可能是更好的方法,因为现在我有 3 个控制器,但肯定会有更多的未来。谢谢
    猜你喜欢
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-25
    • 2018-12-08
    • 2017-04-22
    • 1970-01-01
    • 2021-06-04
    相关资源
    最近更新 更多