FromPath
значение поставщика
То, что вы хотите, это связать сложную модель с частью пути URL. К сожалению, ASP.NET Core не имеет встроенного связывателя FromPath
. К счастью, однако, мы можем построить свой собственный.
Вот пример FromPathValueProvider
в GitHub , который имеет следующий результат:
По сути, это обязательный domain.com/controller/action/key/value/key/value/key/value
. Это отличается от того, что делают провайдеры значений FromRoute
или FromQuery
.
Использовать FromPath
значение поставщика
Создайте такой маршрут:
routes.MapRoute(
name: "properties-search",
template: "{controller=Properties}/{action=Search}/{*path}"
);
Добавьте атрибут [FromPath]
к своему действию:
public IActionResult Search([FromPath]BedsEtCetera model)
{
return Json(model);
}
И волшебным образом он свяжет *path
со сложной моделью:
public class BedsEtCetera
{
public int Beds { get; set; }
public int Page { get; set; }
public string Sort { get; set; }
}
Создание FromPath
поставщика значений
Создать новый атрибут на основе FromRoute
.
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property,
AllowMultiple = false, Inherited = true)]
public class FromPath : Attribute, IBindingSourceMetadata, IModelNameProvider
{
/// <inheritdoc />
public BindingSource BindingSource => BindingSource.Custom;
/// <inheritdoc />
public string Name { get; set; }
}
Создайте новую базу IValueProviderFactory на RouteValueProviderFactory
.
public class PathValueProviderFactory : IValueProviderFactory
{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
var provider = new PathValueProvider(
BindingSource.Custom,
context.ActionContext.RouteData.Values);
context.ValueProviders.Add(provider);
return Task.CompletedTask;
}
}
Создание новой базы IValueProvider на RouteValueProvider
.
public class PathValueProvider : IValueProvider
{
public Dictionary<string, string> _values { get; }
public PathValueProvider(BindingSource bindingSource, RouteValueDictionary values)
{
if(!values.TryGetValue("path", out var path))
{
var msg = "Route value 'path' was not present in the route.";
throw new InvalidOperationException(msg);
}
_values = (path as string).ToDictionaryFromUriPath();
}
public bool ContainsPrefix(string prefix) => _values.ContainsKey(prefix);
public ValueProviderResult GetValue(string key)
{
key = key.ToLower(); // case insensitive model binding
if(!_values.TryGetValue(key, out var value)) {
return ValueProviderResult.None;
}
return new ValueProviderResult(value);
}
}
PathValueProvider
использует метод расширения ToDictionaryFromUriPath
.
public static class StringExtensions {
public static Dictionary<string, string> ToDictionaryFromUriPath(this string path) {
var parts = path.Split('/');
var dictionary = new Dictionary<string, string>();
for(var i = 0; i < parts.Length; i++)
{
if(i % 2 != 0) continue;
var key = parts[i].ToLower(); // case insensitive model binding
var value = parts[i + 1];
dictionary.Add(key, value);
}
return dictionary;
}
}
Соедините все вместе в вашем Startup
классе.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddMvcOptions(options =>
options.ValueProviderFactories.Add(new PathValueProviderFactory()));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes => {
routes.MapRoute(
name: "properties-search",
template: "{controller=Properties}/{action=Search}/{*path}"
);
});
}
}
Вот рабочий образец на GitHub .