Reputation: 5594
Would it be possibile in asp.net core 2.2 to intercept, for each request, the route data and (possibly) change the values, such as the target controller
/ action
values?
My need is to use a localized url scheme, with two letter culture specification and localized controller / actions names, such as:
/en/contact-us
/it/contatti
/de/kontakte
i don't want to use attribute routing to decorate controllers and actions; instead, i want to capture the route data and basing on culture
and controller/action
, obtain the target controller and action, maybe with a dictionary association.
I've already done such thing in asp.net (not core) mvc.
Until now i'm able to capture routing with this code:
app.Use(async (context, next) =>
{
var routeData = context.GetRouteData();
routeData.Values["controller"] = ....;
routeData.Values["action"] = .....;
await next();
});
but route data is filled only for urls precisely mapped to an existing controller/action else is null; it seems this is due to the AttributeRouting.CreateAttributeMegaRoute
method of asp.net core, which only maps existing controllers/actions.
Upvotes: 1
Views: 2256
Reputation: 25350
Here's a walkaround that works for me:
// if the route matches this pattern, let's say: app.UseMvc(routeBuilder => { routeBuilder.MapRoute("route1",template: "/{controller=Home}/{action=Index}"); }); // else if the route matches `{culture=en-US}/{controller=Home}/{action=Index}` app.UseRouter(routeBuilder =>{ var template = "{culture=en-US}/{controller=Home}/{action=Index}"; routeBuilder.MapMiddlewareRoute(template, appBuilder =>{ appBuilder.Use(async(context, next)=>{ var routeData = context.GetRouteData(); var controller = routeData.Values["controller"] as string; var action= routeData.Values["action"] as string; var culture= routeData.Values["culture"] as string; // get the real backing path according to current route data context.Request.Path = getNormalizedPath(routeData); await next(); }); appBuilder.UseRequestLocalization(); appBuilder.UseMvc(rb=>{ rb.MapRoute(name:"cultureRoute",template:template); }); });// if you have other MVC routes, add them below:// routeBuilder.MapRoute(name:"mvcRoutes",template: "{area:exists}/{controller=Home}/{action=Index}");}); // else if doesn't match the above pattern, let's say: app.UseMvc(routeBuilder => { routeBuilder.MapRoute("route3",template: "/test/mvc/{controller=Home}/{action=Index}"); });
private string getNormalizedPath(RouteData routeData)
{
var culture= routeData.Values["culture"] as string;
var controller = routeData.Values["controller"] as string;
var action= routeData.Values["action"] as string;
controller = ... real controller according to current culture & controller string
action = ... real action according to current culture & controller string
return $"/{culture}/{controller}/{action}";
}
You need custom the getNormalizedPath(routeData)
to get the real path that will be routed to the backing controller/action.
To set the Request Localization Feature automatically according to current route path at the same time, you'll need to insert a RouteDataRequestCultureProvider
:
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("de"),
new CultureInfo("it"),
};
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider());
});
As found by @ʞᴉɯ in the comments, the first UseMvc()
doesn't work well for 2.2. We need change the MVC Compatibility to CompatibilityVersion.Version_2_1
:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
Upvotes: 1