Подтвердить что ты не робот

Ядро Asp.Net получает значение RouteData из URL-адреса

Я собираюсь создать новое приложение mvc для Asp.Net. Я определил маршрут с пользовательским ограничением, который устанавливает текущую культуру приложения из URL-адреса. Я пытаюсь управлять локализацией для своего приложения, создавая пользовательский IRequestCultureProvider, который выглядит следующим образом:

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

Мой MyCustomRequestCultureProvider попадает на каждый запрос, и это нормально. Моя проблема заключается в том, что в конвейере MVC метод DetermineProviderCultureResult от моего провайдера попадает перед процессом маршрутизации, поэтому httpContext.GetRouteValue("language") всегда возвращает значение null.

В предыдущей версии MVC у меня была возможность вручную обработать мой URL-адрес через процесс маршрутизации, сделав это

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

Я не могу найти способ сделать то же самое в новом framewrok прямо сейчас. Кроме того, я хочу использовать данные маршрута, чтобы узнать мои langugae, анализируя мою строку url с некоторыми строковыми функциями, чтобы найти язык не является вариантом.

4b9b3361

Ответ 1

Нет простого способа сделать это, и команда ASP.Net еще не решила реализовать эту функциональность. IRoutingFeature доступен только после того, как MVC выполнил запрос.

Я смог собрать решение, которое должно работать для вас. Это позволит настроить маршруты, которые вы передаете в UseMvc(), а также всю маршрутизацию атрибутов, чтобы заполнить IRoutingFeature. После этого вы можете получить доступ к этому классу через 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);
}

Перенос маршрутов в делегат (для повторного использования), тот же файл/класс:

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

Добавить новое промежуточное ПО:

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

Возможно, вам придется определить этот класс...

public class RoutingFeature : IRoutingFeature
{
    public RouteData RouteData { get; set; }
}

Ответ 2

Основываясь на ответе Эшли Ли, вот оптимизированный подход, который предотвращает дублирование конфигурации маршрута.

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

Это зависит от следующих двух методов, которые должны быть добавлены в класс 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);
    };
}