我希望为我的请求对象属性的名称加上别名,以便这些请求都可以工作并且都转到同一个控制器:
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
它基本上代替了ComplexTypeModelBinder(我很胆小,无法替换它,但是我用相同的条件将其放在前面),除了它试图使用我的新属性来扩展它正在寻找的字段
It basically takes the place of the 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); } } }以及要在客户端项目中引用的属性
And an attribute to go in / be referenced by the client project
using System; namespace MYDOMAIN.Client { [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class BindingAliasAttribute : Attribute { public string Alias { get; } public BindingAliasAttribute(string alias) { Alias = alias; } } }在Startup.cs中配置
Configured in the Startup.cs
public void ConfigureServices(IServiceCollection services) { services ... .AddMvcOptions(options => { AliasModelBinderProvider.Configure(options); ... }) ...用法:
public class SomeRequest { [BindingAlias("f")] public long[] SomeVeryLongNameForSomeKindOfFoo{ get; set; } }导致请求看起来像这样:
leading to a request that looks either like this:
api/controller/action?SomeVeryLongNameForSomeKindOfFoo=1&SomeVeryLongNameForSomeKindOfFoo=2或
api/controller/action?f=1&f=2我将大部分内容放在Web项目中,并将属性放在我的客户端项目中.
I put most things in my web project, and the attribute in my client project.
更多推荐
将模型的属性别名别名为asp.net
发布评论