问题
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
}
});
}
}
}
}
回答1:
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:
- Instead of using specific values when creating new
SelectorModel
s, use a token as described above. - With this in place, you will now be able to access a
version
value fromRouteData
, 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.
来源:https://stackoverflow.com/questions/56090637/get-version-when-using-ipageroutemodelconvention