将模型的属性别名别名为asp.net-core中的其他名称 [英] Alias a model's property to a different name in asp.net-core
问题描述
我希望为我的请求对象属性的名称加上别名,以便这些请求都可以工作并且都转到同一个控制器:
I wish to alias the name of my request object properties, so that these requests both work and both go to the same controller:
myapi/cars?colors=red&colors=blue&colors=green
和myapi/cars?c=red&c=blue&c=green
对于请求对象:
public class CarRequest {
Colors string[] { get; set; }
}
任何人都可以使用新的ModelBinder解决此问题而不必从头开始编写ModelBindings吗?
Has anyone been able to use the new ModelBinders to solve this without having to write ModelBindings from scratch?
和推荐答案
我编写了一个模型活页夹来做到这一点: I wrote a model binder to do this:
这是github上的存储库.您可以将两个nuget软件包添加到代码中,以解决此问题.自述文件中的详细信息
Here's the repo on github. There are two nuget packages you can add to your code that solve this problem. Details in the readme 它基本上代替了 It basically takes the place of the 活页夹: 提供者: 价值提供者: 以及要在客户端项目中引用的属性 And an attribute to go in / be referenced by the client project 在Startup.cs中配置 Configured in the Startup.cs 用法: 导致请求看起来像这样: leading to a request that looks either like this: 或 我将大部分内容放在Web项目中,并将属性放在我的客户端项目中. I put most things in my web project, and the attribute in my client project. 这篇关于将模型的属性别名别名为asp.net-core中的其他名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!ComplexTypeModelBinder
(我很胆小,无法替换它,但是我用相同的条件将其放在前面),除了它试图使用我的新属性来扩展它正在寻找的字段ComplexTypeModelBinder
(I'm too cowardly to replace it, but I slot it in front with identical criteria), except that it tries to use my new attribute to expand the fields it's looking for.using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MYDOMAIN.Client;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;
namespace MYDOMAIN.Web.AliasModelBinder
{
public class AliasModelBinder : ComplexTypeModelBinder
{
public AliasModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, ILoggerFactory loggerFactory,
bool allowValidatingTopLevelNodes)
: base(propertyBinders, loggerFactory, allowValidatingTopLevelNodes)
{
}
protected override Task BindProperty(ModelBindingContext bindingContext)
{
var containerType = bindingContext.ModelMetadata.ContainerType;
if (containerType != null)
{
var propertyType = containerType.GetProperty(bindingContext.ModelMetadata.PropertyName);
var attributes = propertyType.GetCustomAttributes(true);
var aliasAttributes = attributes.OfType<BindingAliasAttribute>().ToArray();
if (aliasAttributes.Any())
{
bindingContext.ValueProvider = new AliasValueProvider(bindingContext.ValueProvider,
bindingContext.ModelName, aliasAttributes.Select(attr => attr.Alias));
}
}
return base.BindProperty(bindingContext);
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;
namespace MYDOMAIN.Web.AliasModelBinder
{
public class AliasModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
foreach (var property in context.Metadata.Properties)
{
propertyBinders.Add(property, context.CreateBinder(property));
}
return new AliasModelBinder(propertyBinders,
(ILoggerFactory) context.Services.GetService(typeof(ILoggerFactory)), true);
}
return null;
}
/// <summary>
/// Setup the AliasModelBinderProvider Mvc project to use BindingAlias attribute, to allow for aliasing property names in query strings
/// </summary>
public static void Configure(MvcOptions options)
{
// Place in front of ComplexTypeModelBinderProvider to replace this binder type in practice
for (int i = 0; i < options.ModelBinderProviders.Count; i++)
{
if (options.ModelBinderProviders[i] is ComplexTypeModelBinderProvider)
{
options.ModelBinderProviders.Insert(i, new AliasModelBinderProvider());
return;
}
}
options.ModelBinderProviders.Add(new AliasModelBinderProvider());
}
}
}
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Primitives;
namespace MYDOMAIN.Web.AliasModelBinder
{
public class AliasValueProvider : IValueProvider
{
private readonly IValueProvider _provider;
private readonly string _originalName;
private readonly string[] _allNamesToBind;
public AliasValueProvider(IValueProvider provider, string originalName, IEnumerable<string> aliases)
{
_provider = provider;
_originalName = originalName;
_allNamesToBind = new[] {_originalName}.Concat(aliases).ToArray();
}
public bool ContainsPrefix(string prefix)
{
if (prefix == _originalName)
{
return _allNamesToBind.Any(_provider.ContainsPrefix);
}
return _provider.ContainsPrefix(prefix);
}
public ValueProviderResult GetValue(string key)
{
if (key == _originalName)
{
var results = _allNamesToBind.Select(alias => _provider.GetValue(alias)).ToArray();
StringValues values = results.Aggregate(values, (current, r) => StringValues.Concat(current, r.Values));
return new ValueProviderResult(values, results.First().Culture);
}
return _provider.GetValue(key);
}
}
}
using System;
namespace MYDOMAIN.Client
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class BindingAliasAttribute : Attribute
{
public string Alias { get; }
public BindingAliasAttribute(string alias)
{
Alias = alias;
}
}
}
public void ConfigureServices(IServiceCollection services)
{
services
...
.AddMvcOptions(options =>
{
AliasModelBinderProvider.Configure(options);
...
})
...
public class SomeRequest
{
[BindingAlias("f")]
public long[] SomeVeryLongNameForSomeKindOfFoo{ get; set; }
}
api/controller/action?SomeVeryLongNameForSomeKindOfFoo=1&SomeVeryLongNameForSomeKindOfFoo=2
api/controller/action?f=1&f=2