Получить версию при использовании IPageRouteModelConvention - PullRequest
4 голосов
/ 11 мая 2019

Некоторое время назад я спросил , как добавить какие-то локализованные URL-адреса , где IPageRouteModelConvention вступил в игру, для меня, идеальным способом.

С этим я могу иметь маршруты на разных языках / именах.


Если я использую www.domain.com/nyheter (шведский) или www.domain.com/sistenytt (норвежский), в RouteData я все равно обнаружу, что использовался маршрут News (RouteData.Values["page"]).

Как мне узнать, какая версия?

Я знаю, что могу проверить / проанализировать context.Request.Path, но мне интересно, есть ли встроенное свойство, которое даст мне его вместо этого.


В startup

services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddRazorPagesOptions(options =>
    {
        options.Conventions.Add(new LocalizedPageRouteModelConvention(new LocalizationService(appsettings.Routes)));
    });

appsettings.Routes считывается с appsettings.json

"Routes": [
  {
    "Page": "/Pages/News.cshtml",
    "Versions": [ "nyheter", "sistenytt" ]
  },
  and so on....
]

Класс

public class LocalizedPageRouteModelConvention : IPageRouteModelConvention
    {
        private ILocalizationService _localizationService;

        public LocalizedPageRouteModelConvention(ILocalizationService localizationService)
        {
            _localizationService = localizationService;
        }

        public void Apply(PageRouteModel model)
        {
            var route = _localizationService.LocalRoutes().FirstOrDefault(p => p.Page == model.RelativePath);
            if (route != null)
            {
                foreach (var option in route.Versions)
                {
                    model.Selectors.Add(new SelectorModel()
                    {
                        AttributeRouteModel = new AttributeRouteModel
                        {
                            Template = option
                        }
                    });
                }
            }
        }
    }

1 Ответ

2 голосов
/ 15 мая 2019

Чтобы получить значение RouteData, вы можете указать токен в шаблоне для маршрута. Например, маршрут {version} добавил бы значение RouteData version, которое будет взято из первого сегмента URL. В вашем примере вы не указываете токен для version, поэтому для него не будет никакого значения RouteData, как вы описали.

Решение вашей конкретной проблемы состоит из двух частей:

  1. Вместо использования определенных значений при создании новых SelectorModel s используйте токен, как описано выше.
  2. Имея это, вы теперь сможете получить доступ к значению version из RouteData, но новая проблема заключается в том, что может быть предоставлено любое значение , независимо от того, было ли оно указано в ваша конфигурация.

Чтобы решить вторую проблему, вы можете обратиться к IActionConstraint. Вот реализация:

public class VersionConstraint : IActionConstraint
{
    private readonly IEnumerable<string> allowedValues;

    public VersionConstraint(IEnumerable<string> allowedValues)
    {
        this.allowedValues = allowedValues;
    }

    public int Order => 0;

    public bool Accept(ActionConstraintContext ctx)
    {
        if (!ctx.RouteContext.RouteData.Values.TryGetValue("version", out var routeVersion))
            return false;

        return allowedValues.Contains((string)routeVersion);
    }
}

VersionConstraint принимает список допустимых значений (например, nyheter, sistenytt) и проверяет, соответствует ли значение version RouteData. Если он не совпадает, то «действие» (на данном этапе это действительно страница) не будет совпадением и в результате получится 404.

С этой реализацией вы можете обновить свою реализацию LocalizedPageRouteModelConvention Apply, чтобы она выглядела следующим образом:

var route = _localizationService.LocalRoutes().FirstOrDefault(p => p.Page == model.RelativePath);
if (route != null)
{
    model.Selectors.Add(new SelectorModel
    {
        AttributeRouteModel = new AttributeRouteModel
        {
            Template = "{version}"
        },
        ActionConstraints =
        {
            new VersionConstraint(route.Versions)
        }
    });
}

Эта реализация добавляет один новый SelectorModel, который настроен со значением Version RouteData и ограничен разрешением только значений, указанных в конфигурации.

...