在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<LocalizeRoute> _routes; public LocalizeRedirectMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { if (IsGetMethod(context) && IsSupportContentType(context)) { IApplicationContextAccessor applicationContextAccessor = context.RequestServices.GetService<IApplicationContextAccessor>(); var setting = applicationContextAccessor.Current.CultureSetting; if (setting.UseUrlCode(context.User.Identity.IsAuthenticated)) { var acitveCultures = context.RequestServices.GetService<ICultureService>().GetActiveCulture(); if (_routes == null) { _routes = context.RequestServices.GetService<IRouteProvider>().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。如果你也有正在考慮多語言的解決方案,可以檢視下面的連結:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.1
原文連結:http://www.zkea.net/codesnippet/detail/middleware-routedata.html