在ASP.Net Core中,如果直接在Middleware中获取RouteData返回的是空值,这是因为RouterMiddleware还没执行。但有些情况下需要获取RouteData,这要怎么做呢?
public async Task Invoke(HttpContext context){ var routeData = context.GetRouteData(); null}
TemplateMatcher
TemplateMatcher是获取路由值的关键类。使用它可以将URL按路由Template解析成RouteData。所以我们可以使用它来获取RouteData。
下面是一个简单的辅助类供参考,如果直接使用可能会有一些性能问题,因为解析路由模板(TemplateParser.Parse(routeTemplate))需要时间,所以应当在实际使用的时候优化它:
public class RouteMatcher{ public RouteValueDictionary Match(string routeTemplate, string requestPath) { var template = TemplateParser.Parse(routeTemplate); var matcher = new TemplateMatcher(template, GetDefaults(template)); var values = matcher.Match(requestPath); return values; } private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate) { var result = new RouteValueDictionary(); foreach (var parameter in parsedTemplate.Parameters) { if (parameter.DefaultValue != null) { result.Add(parameter.Name, parameter.DefaultValue); } } return result; }}
有了这些,就可以在Middleware里面来解析RouteData了。
注意
在解析路由时,应当按照路由的注册的先后顺序来解析,并且在成功解析时退出,这样可以保证和程序匹配时的路由是一致的。并且你应当考虑是否加上使用路由的约束(RouteConstraint)来判断当前的路由模板是否匹配。
应用场景
在纸壳CMS里面,当开启多语言时,用户访问了一个不带语言的地址,应当要自动跳转加上用户对应的语言。所以需要使用Middleware来做跳转,同时需要将用户访问的Url解析成RoteData来判断是否需要跳转。
namespace ZKEACMS.MultiLanguage{ public class LocalizeRedirectMiddleware { class LocalizeRoute { public Easy.Mvc.Route.RouteDescriptor Descriptor { get; set; } public TemplateMatcher TemplateMatcher { get; set; } } private readonly RequestDelegate _next; private List_routes; public LocalizeRedirectMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { if (IsGetMethod(context) && IsSupportContentType(context)) { IApplicationContextAccessor applicationContextAccessor = context.RequestServices.GetService (); var setting = applicationContextAccessor.Current.CultureSetting; if (setting.UseUrlCode(context.User.Identity.IsAuthenticated)) { var acitveCultures = context.RequestServices.GetService ().GetActiveCulture(); if (_routes == null) { _routes = context.RequestServices.GetService ().GetRoutes().OrderByDescending(m => m.Priority).Select(m => { var template = TemplateParser.Parse(m.Template); return new LocalizeRoute { Descriptor = m, TemplateMatcher = new TemplateMatcher(template, GetDefaults(template)) }; }).ToList(); } foreach (var item in _routes) { var routeData = new RouteValueDictionary(); if (item.TemplateMatcher.TryMatch(context.Request.Path.Value, routeData)) { if(item.Descriptor is LocalizeRouteDescriptor) { object cultureCode; if (routeData.TryGetValue("culture", out cultureCode)) { if (!acitveCultures.Any(m => cultureCode.Equals(m.UrlCode))) { context.Response.Redirect($"/{context.GetUserCulture().UrlCode}{context.Request.GetAbsoluteUrl()}"); return Task.CompletedTask; } } else { context.Response.Redirect($"/{context.GetUserCulture().UrlCode}{context.Request.GetAbsoluteUrl()}"); return Task.CompletedTask; } } break; } } } } return _next(context); } private bool IsGetMethod(HttpContext context) { return string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase); } private bool IsSupportContentType(HttpContext context) { return true; } private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate) { var result = new RouteValueDictionary(); foreach (var parameter in parsedTemplate.Parameters) { if (parameter.DefaultValue != null) { result.Add(parameter.Name, parameter.DefaultValue); } } return result; } }}
另外
对于对于多语言的跳转,微软其实有提供了一个Localization middleware,只不过在纸壳CMS的多语言场景里有点不太适用,所以重新写了这个LocalizeRedirectMiddleware。如果你也有正在考虑多语言的解决方案,可以查看下面的链接: