.Net Core在Middleware中解析RouteData

在ASP.Net Core中,若是直接在Middleware中獲取RouteData返回的是空值,這是由於RouterMiddleware還沒執行。但有些狀況下須要獲取RouteData,這要怎麼作呢?app

public async Task Invoke(HttpContext context)
{
    var routeData = context.GetRouteData(); null
}

TemplateMatcher

TemplateMatcher是獲取路由值的關鍵類。使用它能夠將URL按路由Template解析成RouteData。因此咱們能夠使用它來獲取RouteData。async

下面是一個簡單的輔助類供參考,若是直接使用可能會有一些性能問題,由於解析路由模板(TemplateParser.Parse(routeTemplate))須要時間,因此應當在實際使用的時候優化它:ide

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來判斷是否須要跳轉。spa

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。若是你也有正在考慮多語言的解決方案,能夠查看下面的連接:code

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.1ip

相關文章
相關標籤/搜索