使用 IPageRouteModelConvention 时获取版本

Get version when using IPageRouteModelConvention

我前段时间问过 IPageRouteModelConvention 是否以对我来说完美的方式发挥作用。

这样我就可以在不同的 languages/names.

中设置路由

如果我使用 www.domain.com/nyheter(瑞典语)或 www.domain.com/sistenytt(挪威语),我仍然只发现在 RouteData 中使用了 News 路由(RouteData.Values["page"]).

我如何获得哪个版本?

我知道我可以 check/parse 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....
]

class

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
                        }
                    });
                }
            }
        }
    }

要检索 RouteData 值,您可以在路由模板中指定一个标记。例如,路线 {version} 将添加取自 URL 的第一段的 versionRouteData 值。在您的示例中,您没有为 version 指定标记,因此正如您所描述的,它不会有 RouteData 值。

针对您的特定问题的解决方案是 two-part:

  1. 在创建新的 SelectorModel 时不要使用特定值,而是使用如上所述的标记。
  2. 有了这个,您现在可以从 RouteData 访问 version 值,但新的问题是 any 值可以提供,无论是否在您的配置中指定。

要解决第二个问题,可以转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 获取允许值列表(例如 nyhetersistenytt)并检查 version RouteData 值是否匹配。如果不匹配,"action"(此时它实际上是一个页面)将不匹配并以 404 结束。

有了该实施,您可以将 LocalizedPageRouteModelConventionApply 的实施更新为如下所示:

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,该 SelectorModel 设置了 Version RouteData 值,并且被限制为仅允许配置中指定的值。