web-dev-qa-db-ja.com

Asp.NetコアはURLからRouteData値を取得します

新しいAsp.Netコアmvc​​アプリを探しています。 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")
_

私は今、新しいフレームワークで同じことをする方法を見つけることができません。また、ルートデータを使用して言語を見つけ、URL文字列をいくつかの文字列関数で分析して、言語を見つけることはオプションではありません。

25

これを行う簡単な方法はなく、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; }
}
12
Ashley Lee

Ashley Leeの答えに基づいて、重複したルート構成を防ぐ最適化されたアプローチを以下に示します。

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クラスに追加する必要がある次の2つのメソッドに依存します。

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