• ASP.NET Core 3.0动态控制器路由


    前言

    相对于ASP.NET MVC以及ASP.NET Core MVC中的旧版本路由特性, 在ASP.NET Core 3.0中新增了一个不错的扩展点,即程序获取到路由后,可以将其动态指向一个给定的controller/action.

    这个功能有非常多的使用场景。如果你正在使用从ASP.NET Core 3.0 Preview 7及更高版本,你就可以在ASP.NET Core 3.0中使用它了。

    背景

    当我们使用MVC路由的时候,最典型的用法是,我们使用路由特性、EndPoint Route两种方式

    以上两种方式的共同点是,所有的路由信息都必须在应用程序启动时加载。

    但是,如果你希望能够动态定义路由, 并在应用程序运行时添加/删除它们,该怎么办?

    下面我给大家列举几个动态定义路由的使用场景。

    • 多语言路由,以及使用新语言时,针对那些新语言路由的修改。
    • 在CMS类型的系统中,我们可能会动态添加一些新页面,这些新页面不需要创建的控制器或者在源码中硬编码路由信息。
    • 多租户应用中,租户路由可以在运行时动态激活或者取消激活。

    这个问题的处理过程应该相当的好理解。我们希望尽早的拦截路由处理,检查已为其解析的当前路由值,并使用例如数据库中的数据将它们“转换”为一组新的路由值,这些新的路由值指向了一个实际存在的控制器。

    实例问题 - 多语言翻译路由问题

    在旧版本的ASP.NET Core MVC中, 我们通常通过自定义IRouter接口,来解决这个问题。然而在ASP.NET Core 3.0中这种方式已经行不通了,因为路由已经改由上面提到的Endpoint Routing来处理。值得庆幸的是,ASP.NET Core 3.0 Preview 7以及后续版本中,我们可以通过一个新特性MapDynamicControllRoute以及一个扩展点DynamicRouteValueTransformer, 来支持我们的需求。下面让我们看一个具体的例子。

    想象一下,在你的项目中,有一个OrderController控制器,然后你希望它支持多语言翻译路由。

    public class OrdersController : Controller
    {
        public IActionResult List()
        {
            return View();
        }
    }

    我们可能希望的请求的URL是这样的,例如

    • 英语 - /en/orders/list
    • 德语 - /de/bestellungen/liste
    • 波兰语 - /pl/zamowienia/lista

    使用动态路由处理多语言翻译路由问题

    我们可以使用新特性MapDynamicControllerRoute来替代默认的MVC路由, 并将其指向我们自定义的DynamicRouteValueTransformer类, 该类实现了我们之前提到的路由值转换 。

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<TranslationTransformer>();
            services.AddSingleton<TranslationDatabase>();
        }
    
        public void Configure(IApplicationBuilder app)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDynamicControllerRoute<TranslationTransformer>("{language}/{controller}/{action}");
            });
        }
    }

    这里我们定义了一个TranslationTransformer类,它继承了DynamicRouteValueTransformer类。这个新类将负责将特定语言路由值,转换为可以在我们应用可以匹配到controller/action的路由值字典,而这些值通常不能直接和我们应用中的任何controller/action匹配。在德语场景下,controller名会从“Bestellungen”转换成"Orders", action名"Liste"转换成"List"。

    TranslationTransformer类被作为泛型类型参数,传入MapDynamicControllerRoute方法中,它必须在依赖注入容器中注册。这里,我们还需要注册一个TranslationDatabase类,但是这个类仅仅为了帮助演示,后面我们会需要它。

    public class TranslationTransformer : DynamicRouteValueTransformer
    {
        private readonly TranslationDatabase _translationDatabase;
    
        public TranslationTransformer(TranslationDatabase translationDatabase)
        {
            _translationDatabase = translationDatabase;
        }
    
        public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext
        , RouteValueDictionary values)
        {
            if (!values.ContainsKey("language") 
                || !values.ContainsKey("controller") 
                || !values.ContainsKey("action")) return values;
    
            var language = (string)values["language"];
            var controller = await _translationDatabase.Resolve(language, 
                (string)values["controller"]);
                
            if (controller == null) return values;
            values["controller"] = controller;
    
            var action = await _translationDatabase.Resolve(language, 
                (string)values["action"]);
                
            if (action == null) return values;
            values["action"] = action;
    
            return values;
        }
    }

    在这个转换器中,我们需要尝试提取3个路由参数, languagecontroller,action,然后我们需要在模拟用的数据库类中,找到其对应的翻译。正如我们之前提到的,你通常会希望从数据库中查找对应的内容,因为使用这种方式,我们可以在应用程序生命周期的任何时刻,动态的影响路由。

  • 相关阅读:
    js原型杂谈
    arguments.callee.caller
    $resource
    sql的四种匹配模式
    AMD规范
    module.ngdoc
    angularjs杂谈
    浏览器前缀
    css21规范学习
    <meta>标签
  • 原文地址:https://www.cnblogs.com/fanfan-90/p/12382935.html
Copyright © 2020-2023  润新知