web-dev-qa-db-fra.com

Le noyau Asp.Net obtient la valeur RouteData de l'url

Je travaille sur une nouvelle application mvc principale Asp.Net. J'ai défini un itinéraire avec une contrainte personnalisée, qui définit la culture actuelle de l'application à partir de l'URL. J'essaie de gérer la localisation de mon application en créant un IRequestCultureProvider personnalisé qui ressemble à ceci:

public class MyCustomRequestCultureProvider : IRequestCultureProvider
    {
        public Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
        {
            var language= httpContext.GetRouteValue("language");

            var result = new ProviderCultureResult(language, language);
            return Task.FromResult(result);
        }
    }

Mon MyCustomRequestCultureProvider est touché à chaque demande, ce qui est correct. Mon problème est que dans le pipeline MVC, la méthode DetermineProviderCultureResult de mon fournisseur est frappée avant le processus de routage, donc httpContext.GetRouteValue("language") retourne toujours null.

Dans la version précédente de MVC, j'avais la possibilité de traiter manuellement mon URL via le processus de routage en faisant ceci

var wrapper = new HttpContextWrapper(HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(wrapper);
var language = routeData.GetValue("language")

Je ne trouve pas de moyen de faire la même chose dans le nouveau framewrok en ce moment. En outre, je veux utiliser les données de l'itinéraire pour découvrir mes langues, analyser ma chaîne d'URL avec certaines fonctions de chaîne pour trouver la langue n'est pas une option.

25
Nicolas Boisvert

Il n'y a pas de moyen facile de le faire, et l'équipe ASP.Net n'a pas encore décidé de mettre en œuvre cette fonctionnalité. IRoutingFeature n'est disponible qu'une fois que MVC a terminé la demande.

J'ai cependant pu trouver une solution qui devrait fonctionner pour vous. Cela configurera les routes que vous passez dans UseMvc() ainsi que tous les routages d'attributs afin de remplir IRoutingFeature. Une fois cette opération terminée, vous pouvez accéder à cette classe via httpContext.GetRouteValue("language");.

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // setup routes
    app.UseGetRoutesMiddleware(GetRoutes);

    // add localization
    var requestLocalizationOptions = new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture("en-US")
    };
    requestLocalizationOptions.RequestCultureProviders.Clear();
    requestLocalizationOptions.RequestCultureProviders.Add(
        new MyCustomRequestCultureProvider()
    );
    app.UseRequestLocalization(requestLocalizationOptions);

    // add mvc
    app.UseMvc(GetRoutes);
}

Déplacement des routes vers un délégué (pour réutilisation), même fichier/classe:

private readonly Action<IRouteBuilder> GetRoutes =
    routes =>
    {
        routes.MapRoute(
            name: "custom",
            template: "{language=fr-FR}/{controller=Home}/{action=Index}/{id?}");

        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    };

Ajouter un nouveau middleware:

public static class GetRoutesMiddlewareExtensions
{
    public static IApplicationBuilder UseGetRoutesMiddleware(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        var routes = new RouteBuilder(app)
        {
            DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
        };
        configureRoutes(routes);
        routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
        var router = routes.Build();

        return app.UseMiddleware<GetRoutesMiddleware>(router);
    }
}

public class GetRoutesMiddleware
{
    private readonly RequestDelegate next;
    private readonly IRouter _router;

    public GetRoutesMiddleware(RequestDelegate next, IRouter router)
    {
        this.next = next;
        _router = router;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var context = new RouteContext(httpContext);
        context.RouteData.Routers.Add(_router);

        await _router.RouteAsync(context);

        if (context.Handler != null)
        {
            httpContext.Features[typeof (IRoutingFeature)] = new RoutingFeature()
            {
                RouteData = context.RouteData,
            };
        }

        // proceed to next...
        await next(httpContext);
    }
}

Vous devrez peut-être également définir cette classe ...

public class RoutingFeature : IRoutingFeature
{
    public RouteData RouteData { get; set; }
}
12
Ashley Lee

Sur la base de la réponse d'Ashley Lee, voici une approche optimisée qui empêche la configuration de route en double.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
   // setup routes
   var mvcRouter = BuildMvcRouter(app, routes =>
   {
       routes.MapRoute(
           name: "custom",
           template: "{language=fr-FR}/{controller=Home}/{action=Index}/{id?}");
       routes.MapRoute(
           name: "default",
           template: "{controller=Home}/{action=Index}/{id?}");
    });

    // add route data initialization middleware
    app.Use(next => SetRouteData(next, mvcRouter));

    // add localization middleware
    var requestLocalizationOptions = new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture("en-US")
    };
    requestLocalizationOptions.RequestCultureProviders.Clear();
    requestLocalizationOptions.RequestCultureProviders.Add(
        new MyCustomRequestCultureProvider()
    );
    app.UseRequestLocalization(requestLocalizationOptions);

    // add mvc routing middleware
    app.UseRouter(mvcRouter);
}

Cela dépend des deux méthodes suivantes qui doivent être ajoutées à la classe StartUp:

private static IRouter BuildMvcRouter(IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
{
    if (app == null) throw new ArgumentNullException(nameof(app));
    if (configureRoutes == null) throw new ArgumentNullException(nameof(configureRoutes));

    app.ApplicationServices.GetRequiredService<MiddlewareFilterBuilder>().ApplicationBuilder = app.New();
    var routeBuilder = new RouteBuilder(app)
    {
        DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>()
    };
    configureRoutes(routeBuilder);
    routeBuilder.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));

    return routeBuilder.Build();
}

private static RequestDelegate SetRouteData(RequestDelegate next, IRouter router)
{
    return async context =>
    {
        var routeContext = new RouteContext(context);
        await router.RouteAsync(routeContext);

        if (routeContext.Handler != null)
        {
            context.Features[typeof(IRoutingFeature)] = new RoutingFeature
            {
                RouteData = routeContext.RouteData
            };
        }

        await next(context);
    };
}
3
Gerke Geurts