ASP.NET Core 3.0 using dynamic routing controller

Original: Dynamic routing the Controller in ASP.NET 3.0 Core
Author: Filip W
Translation: https://www.cnblogs.com/lwqlun/p/11461657.html
Translator: Lamond Lu

Translator's Note

Today the Internet to see this article on ASP.NET Core dynamic routing, feeling very interesting for everyone to translate, although the examples herein do not necessarily occur in everyday coding, but also provides us with some ideas.

Foreword

After relative to the older version of ASP.NET MVC and ASP.NET Core MVC routing features in, added a nice extension point in ASP.NET Core 3.0, a program that is to get the route, it can be dynamically point to a given the controller / action.

This feature has a lot of usage scenarios. If you are using ASP.NET Core 3.0 Preview and later from the 7, you can use it in ASP.NET Core 3.0 in.

PS: The official did not mention this in the Release Notes.

Let's take a look at the dynamic routing in ASP.NET Core 3.0.

background

When we use the MVC routes, the most typical usage is, we use the routing features (Route Attributes) to define the routing information. Using this method, we need to be explicitly declared for each route.

public class HomeController : Controller
{
   [Route("")]
   [Route("Home")]
   [Route("Home/Index")]
   public IActionResult Index()
   {
      return View();
   }
}

In contrast, you can use the center of the route model, using this approach, you do not need to explicitly declare each of the routes - these are automatically routed automatically identify all controllers found. However, to do this premise is that all controllers must exist.

The following is an ASP.NET Core 3.0 using the new syntax Endpoint Routing implementation.

app.UseEndpoints(
    endpoints =>
    {
        endpoints.MapControllerRoute("default", 
                  "{controller=Home}/{action=Index}/{id?}");
    }
);

Above two methods in common is that all the routing information must be loaded when the application starts.

However, if you want to be able to define dynamic routing and add / remove them when the application is running, how to do?

Now I give you define dynamic routing to name a few usage scenarios.

  • Multi-language route, as well as when using the new language, the language modified for those new routes.
  • In the CMS type of system, we might add some new dynamic pages, these controllers do not need to create new pages or routing information hard-coded in the source code.
  • Multi-tenant applications, tenant dynamic routing can be activated or deactivated at runtime.

Processing of this issue should be reasonably well understood. We hope as soon as possible to intercept routing process, check the value of its current routing resolution, and uses the data in the database, for example, they will be "converted" into a new set of route values, the value of these new routes point to the existence of an actual control device.

Examples of questions - multiple language translation routing problem

In older versions of ASP.NET Core MVC, we usually via a custom IRouterinterface to solve this problem. However, in ASP.NET Core 3.0 it has not work in this way, since the route has been changed by the Endpoint Routing processed mentioned above. Thankfully, 3.0 Preview 7 and subsequent versions of ASP.NET Core, we can pass a new feature MapDynamicControllRouteand an extension point DynamicRouteValueTransformerto support our needs. Let's look at a concrete example.

Imagine in your project, there is a OrderControllercontroller, then you want it to support multi-language translation routing.

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

We may want to request URL is the case, for example,

  • English - / EN / the Orders / List
  • German - / de / bestellungen / Liste
  • Polish - / PL / zamowienia / lista

Using dynamic routing handle multi-language translation routing problem

So now how do we solve this problem? We can use the new features MapDynamicControllerRouteto replace the default MVC route, and point it to our custom DynamicRouteValueTransformerclass that implements the routing conversion value we mentioned before.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest);

        services.AddSingleton<TranslationTransformer>();
        services.AddSingleton<TranslationDatabase>();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDynamicControllerRoute<TranslationTransformer>("{language}/{controller}/{action}");
        });
    }
}

Here we define a TranslationTransformerclass that inherits the DynamicRouteValueTransformerclass. This new class will be responsible for the specific language route values, we can convert the application to match controller / action route value dictionary, but these values are not usually any controller and our direct application in the / action match. So here simply put, is under German scene, controller name will be converted from "Bestellungen" to "Orders", action name "Liste" is converted to "List".

TranslationTransformerClass is used as a generic type parameter passing MapDynamicControllerRoutemethod, it must be injected into the vessel register dependency. Here, we also need to sign up for a TranslationDatabaseclass, but the class only to help demonstrate, we will need it later.

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

In this converter, we need to try to extract 3 routing parameters, language, controller, actionand then we need to use the database class simulation, find the corresponding translation. As we mentioned before, you would normally expect to find the corresponding content from the database, because this way, we can at any time of the application life cycle, dynamic effects routing. To illustrate this point, we will use the TranslationDatabaseclass to simulate the operation of the database, where you can think of it as a real database warehousing services.

public class TranslationDatabase
{
    private static Dictionary<string, Dictionary<string, string>> Translations 
        = new Dictionary<string, Dictionary<string, string>>
    {
        {
            "en", new Dictionary<string, string>
            {
                { "orders", "orders" },
                { "list", "list" }
            }
        },
        {
            "de", new Dictionary<string, string>
            {
                { "bestellungen", "orders" },
                { "liste", "list" }
            }
        },
        {
            "pl", new Dictionary<string, string>
            {
                { "zamowienia", "order" },
                { "lista", "list" }
            }
        },
    };

    public async Task<string> Resolve(string lang, string value)
    {
        var normalizedLang = lang.ToLowerInvariant();
        var normalizedValue = value.ToLowerInvariant();
        if (Translations.ContainsKey(normalizedLang) 
            && Translations[normalizedLang]
                .ContainsKey(normalizedValue))
        {
            return Translations[normalizedLang][normalizedValue];
        }

        return null;
    }
}

So far, we have a good solution to this problem. By enabling this setting here in MVC applications, we can assure we defined three routes to send the request.

  • English - / EN / the Orders / List
  • German - / de / bestellungen / Liste
  • Polish - / PL / zamowienia / lista

Each request will hit OrderControllercontroller and Listmethod. The current you can use this method further extended to other controllers. But the most important thing is, if a new language to add new routes or alias mapped to an existing language controller / actions, you do not need to make any code changes, or even restart the project.

Note that in this article, we focus only route translation, here merely to demonstrate the dynamic routing features in ASP.NET Core 3.0. If you hope to achieve localization in your application, you may also want to read ASP.NET Core 3.0 of localization guide , because you may need to set the correct routing based on the language of values CurrentCulture.

Finally, I would like to add that, in our previous example, we explicitly used in routing templates {controller}and {action}placeholders. This is not necessary, in other scenes, you can also use "catch-all"routing wildcard, and convert it to controller / action route values.

"catch-all"CMS system routing wildcard is a typical solution, you can use it to handle different dynamic "pages" route.

It might look something like:

endpoints.MapDynamicControllerRoute<PageTransformer>("pages/{**slug}");

Then, you will need pagesthe entire URL parameters after conversion into an existing executable content controller - usually URL / Route maps are stored in the database.

I hope you will find this article useful - all demo source code are available on Github found on.

Guess you like

Origin www.cnblogs.com/lwqlun/p/11461657.html