将模型的属性别名别名为asp.net-core中的其他名称 [英] Alias a model's property to a different name in asp.net-core

查看:185
本文介绍了将模型的属性别名别名为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=greenmyapi/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-core中的其他名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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