Asp.Net 核心从 url 获取 RouteData 值
Asp.Net core get RouteData value from url
我正在开发一个新的 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")
总是 return null。
在之前版本的 MVC 中,我可以通过路由过程手动处理我的 url
var wrapper = new HttpContextWrapper(HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(wrapper);
var language = routeData.GetValue("language")
我现在找不到在新框架中做同样事情的方法。另外,我想使用路由数据找出我的语言,用一些字符串函数分析我的 url 字符串来查找语言不是一个选项。
没有简单的方法可以做到这一点,ASP.Net 团队还没有决定实现这个功能。 IRoutingFeature
仅在 MVC 完成请求后可用。
我能够整理出一个适合您的解决方案。这将设置您传递给 UseMvc()
的路由以及所有属性路由,以便填充 IRoutingFeature。完成后,您可以通过 httpContext.GetRouteValue("language");
.
访问 class
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);
}
将路由移至委托(为了可重用性),相同 file/class:
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);
}
}
您可能还需要定义此 class...
public class RoutingFeature : IRoutingFeature
{
public RouteData RouteData { get; set; }
}
根据 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);
}
这取决于必须添加到启动程序中的以下两个方法class:
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);
};
}
添加端点路由功能后,这变得更加容易。
本文介绍了如何使用终结点路由功能实现此目的 https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/
var endpointFeature = context.Features[typeof(Microsoft.AspNetCore.Http.Features.IEndpointFeature)]
as Microsoft.AspNetCore.Http.Features.IEndpointFeature;
Microsoft.AspNetCore.Http.Endpoint endpoint = endpointFeature?.Endpoint;
//Note: endpoint will be null, if there was no
//route match found for the request by the endpoint route resolver middleware
if (endpoint != null)
{
var routePattern = (endpoint as Microsoft.AspNetCore.Routing.RouteEndpoint)?.RoutePattern
?.RawText;
Console.WriteLine("Name: " + endpoint.DisplayName);
Console.WriteLine($"Route Pattern: {routePattern}");
Console.WriteLine("Metadata Types: " + string.Join(", ", endpoint.Metadata));
}
我正在开发一个新的 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")
总是 return null。
在之前版本的 MVC 中,我可以通过路由过程手动处理我的 url
var wrapper = new HttpContextWrapper(HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(wrapper);
var language = routeData.GetValue("language")
我现在找不到在新框架中做同样事情的方法。另外,我想使用路由数据找出我的语言,用一些字符串函数分析我的 url 字符串来查找语言不是一个选项。
没有简单的方法可以做到这一点,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);
}
将路由移至委托(为了可重用性),相同 file/class:
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);
}
}
您可能还需要定义此 class...
public class RoutingFeature : IRoutingFeature
{
public RouteData RouteData { get; set; }
}
根据 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);
}
这取决于必须添加到启动程序中的以下两个方法class:
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);
};
}
添加端点路由功能后,这变得更加容易。 本文介绍了如何使用终结点路由功能实现此目的 https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/
var endpointFeature = context.Features[typeof(Microsoft.AspNetCore.Http.Features.IEndpointFeature)]
as Microsoft.AspNetCore.Http.Features.IEndpointFeature;
Microsoft.AspNetCore.Http.Endpoint endpoint = endpointFeature?.Endpoint;
//Note: endpoint will be null, if there was no
//route match found for the request by the endpoint route resolver middleware
if (endpoint != null)
{
var routePattern = (endpoint as Microsoft.AspNetCore.Routing.RouteEndpoint)?.RoutePattern
?.RawText;
Console.WriteLine("Name: " + endpoint.DisplayName);
Console.WriteLine($"Route Pattern: {routePattern}");
Console.WriteLine("Metadata Types: " + string.Join(", ", endpoint.Metadata));
}