使用IPageRouteModelConvention时获取版本 [英] Get version when using IPageRouteModelConvention

查看:82
本文介绍了使用IPageRouteModelConvention时获取版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我前段时间问过如何添加某种本地化版本网址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.json

读取

appsettings.Routes

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

解决方案

要检索RouteData值,可以在模板的路由中指定标记.例如,路由{version}将添加一个RouteDataversion,该值将从URL的第一段中获取.在您的示例中,您没有为version指定令牌,因此正如您所描述的那样,将没有RouteData值.

针对您的特定问题的解决方案分为两部分:

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

要解决第二个问题,可以转到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值是否匹配.如果不匹配,则操作"(此时实际上是一个页面)将不匹配,并以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值,并且被限制为仅允许在配置中指定的值.

I sometime ago asked how to add some kind of localized url's, were IPageRouteModelConvention came into play in a, for me, perfect way.

With that I'm able to have routes in different languages/names.


If I use www.domain.com/nyheter (swedish) or www.domain.com/sistenytt (norwegian) I still only find, in RouteData, that the News route were used (RouteData.Values["page"]).

How do I get which version?

I know I can check/parse the context.Request.Path but am wondering if there is a built-in property that will give me it instead.


In startup

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


appsettings.Routes is read from appsettings.json

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


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

解决方案

To retrieve a RouteData value, you can specify a token within the template for a route. For example, the route {version} would add a RouteData value of version that would be taken from the URL's first segment. In your example, you don't specify a token for version and so there will be no RouteData value for it, as you've described.

The solution for your specific problem is two-part:

  1. Instead of using specific values when creating new SelectorModels, use a token as described above.
  2. With this in place, you will now be able to access a version value from RouteData, but the new problem is that any value can be provided, whether or not it was specified in your configuration.

To solve the second problem, you can turn to IActionConstraint. Here's an implementation:

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 takes a list of allowed values (e.g. nyheter, sistenytt) and checks whether or not the version RouteData value matches. If it doesn't match, the "action" (it's really a page at this point) won't be a match and will end up with a 404.

With that implementation in place, you can update your implementation of LocalizedPageRouteModelConvention's Apply to look like this:

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

This implementation adds a single new SelectorModel that's set up with a Version RouteData value and is constrained to only allow the values specified in configuration.

这篇关于使用IPageRouteModelConvention时获取版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆