ABP框架使用(版本3.3.1) - Feature module

{{baseUrl}}/api/feature-management/features?providerName=D&providerKey=

1. 必须给对应的Provider给予Policy,不然会报错

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<FeatureManagementOptions>(options =>
        {
            options.ProviderPolicies[DefaultValueFeatureValueProvider.ProviderName] = AppNet6Permissions.Products.Default;
        });
    }

2. 新加个FeatureDefinitionProvider,添加Feature值

    public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
    {
        public override void Define(IFeatureDefinitionContext context)
        {
            var myGroup = context.AddGroup("MyApp");

            var reportingFeature = myGroup.AddFeature(
                "MyApp.Reporting",
                defaultValue: "false",
                displayName: LocalizableString
                                 .Create<AppNet6Resource>("Reporting"),
                valueType: new ToggleStringValueType()
            );

            reportingFeature.CreateChild(
                "MyApp.PdfReporting",
                defaultValue: "false",
                displayName: LocalizableString
                                 .Create<AppNet6Resource>("PdfReporting"),
                valueType: new ToggleStringValueType()
            );

            reportingFeature.CreateChild(
                "MyApp.ExcelReporting",
                defaultValue: "false",
                displayName: LocalizableString
                                 .Create<AppNet6Resource>("ExcelReporting"),
                valueType: new ToggleStringValueType()
            );
        }
    }

3. 所用的ProviderName跟最后出来的value有关,DefaultValueFeatureValueProvider.ProviderName 的值是“D"

如果Provider是用别的值,那么API返回的value会是空.

Default的不会查询表AbpFeatureValues,也可以新起个provider从数据库取值

可以查看FeatureManager的代码如下

 

 

 

 4.这里的IValueType是dynamic的,现成的有ToggleStringValueType,SelectionStringValueType,FreeTextStringValueType,对于于UI上的CheckBox,DropdownList和InputBox 。如果我想定义多一些属性给SelectionStringValueType,可以继承ISelectionStringValueItem。

    public interface IMySelectionStringValueItemSource
    {
        ICollection<MySelectionStringValueItem> Items { get; }
    }
    public class MySelectionStringValueItemSource : IMySelectionStringValueItemSource
    {

        public ICollection<MySelectionStringValueItem> Items { get; }

  
        public MySelectionStringValueItemSource(params MySelectionStringValueItem[] items)
        {
            Items = Check.NotNullOrEmpty(items, nameof(items));
        }
    }
    [Serializable]
    [StringValueType("MYSELECTION")]
    public class MySelectionStringValueType : StringValueTypeBase
    {
        public MySelectionStringValueItemSource ItemSource { get; set; }

        public MySelectionStringValueType()
        {

        }

        public MySelectionStringValueType(IValueValidator validator)
            : base(validator)
        {

        }
    }
    public class MySelectionStringValueItem : ISelectionStringValueItem
    {
        public string Value { get; set; }

        public string Label { get; set; }

        public string Source { get; set; }
        public LocalizableStringInfo DisplayText { get; set; }

        public MySelectionStringValueItem(string value, string label, string source)
        {
            Value = value;
            Label = label;
            Source = source;
            DisplayText = null; 
        }
    }

MyFeatureDefinitionProvider

           myGroup.AddFeature(
                "MyApp.Product",
                defaultValue: "name2",
                displayName: LocalizableString
                                 .Create<AppNet6Resource>("MaxProductCount"),
                // valueType: new FreeTextStringValueType(
                //               new NumericValueValidator(0, 1000000)),
                valueType : new MySelectionStringValueType()
                {
                    ItemSource = new MySelectionStringValueItemSource(
                         new MySelectionStringValueItem("name", "label","source")
                        , new MySelectionStringValueItem("name2", "label", "source")
                        , new MySelectionStringValueItem("name3", "label", "source"))
                }

            );

还不清楚  properties 怎么用

 

 5.UI Selection 用Enum类型会更适合,利用 DisplayAttribute,可以定义Name,Description,GroupName,ShortName给前端使用

MySelectionStringValueItem

    public class MySelectionStringValueItem : ISelectionStringValueItem
    {

        public int? Order { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string GroupName { get; set; }
        public string ShortName { get; set; }
        public string Value { get; set; }

        public LocalizableStringInfo DisplayText { get; set; }

        public MySelectionStringValueItem()
        {

        }


        public MySelectionStringValueItem(int? order, string name, string value = null, string description = null, string groupName = null, string shortName = null, LocalizableStringInfo displayText = null)
        {
            Order = order;
            Name = name;
            Value = value ?? Name;
            //Description = description ?? Name;
            //GroupName = groupName ?? Name;
            //ShortName = shortName ?? Name;
            //DisplayText = displayText ?? new LocalizableStringInfo(null, Name);
            Description = description;
            GroupName = groupName ;
            ShortName = shortName ;
            DisplayText = displayText;
        }
    }

接着将Enum Type转换成 MySelectionStringValueItem

EnumHelper

    public static class EnumHelper
    {
        public static MySelectionStringValueItem[] ConvertEnumToArray(Type enumType)
        {
            MySelectionStringValueItem[] items = new MySelectionStringValueItem[Enum.GetNames(enumType).Length];
            FieldInfo[] fieldInfos = enumType.GetFields();
            int i = 0;
            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                if (fieldInfo.FieldType != enumType) continue;
                DisplayAttribute[] attributes = (DisplayAttribute[])fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false);

                if (attributes.Length > 0)
                {
                    var attribute = attributes[0];
                    int order = attribute.GetOrder() == null ? 0 : attribute.GetOrder().Value;
                    items[i] = new MySelectionStringValueItem(order: attribute.GetOrder()
                                                                , name: attribute.Name ?? fieldInfo.Name
                                                                , value: fieldInfo.GetValue(null).GetHashCode().ToString()
                                                                , description: attribute.Description
                                                                , groupName: attribute.GroupName
                                                                , shortName: attribute.ShortName
                                                                , displayText: null);
                }
                else
                {
                    items[i] = new MySelectionStringValueItem(order: null
                                                                , name: fieldInfo.Name
                                                                , value: fieldInfo.GetValue(null).GetHashCode().ToString()
                                                                , description: null
                                                                , groupName: null
                                                                , shortName: null
                                                                , displayText: null);
                }
                i++;
            }
            return items;
        }
    }

MyFeatureDefinitionProvider

public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
    {
        public override void Define(IFeatureDefinitionContext context)
        {
            var myGroup = context.AddGroup("MyApp");

            myGroup.AddFeature(
                "MyApp.Product",
                defaultValue: "name2",
                displayName: LocalizableString
                                 .Create<AppNet6Resource>("MaxProductCount"),
                // valueType: new FreeTextStringValueType(
                //               new NumericValueValidator(0, 1000000)),
                valueType: new MySelectionStringValueType()
                {
                    ItemSource = new MySelectionStringValueItemSource(
                    //     new MySelectionStringValueItem("name", "label", "source")
                    //    , new MySelectionStringValueItem("name2", "label", "source")
                    //    , new MySelectionStringValueItem("name3", "label", "source")
                     EnumHelper.ConvertEnumToArray(typeof(TestEnum))
                    )
                }

            );
        }



        public enum TestEnum
        {
            [Display(Name = "TestEnum1", Order = 1, Description = "TestEnum1 Description")]
            TestEnumOne = -1,
            [Display(Order = 2, Description = "TestEnum2 Description")]
            TestEnumFour = 4,
            [Display(Name = "TestEnum15", Order = 3, Description = "TestEnum3 Description")]
            TestEnumFive = 5
        }
    }

 

 6.既然 Feature里的data可以利用Attribute转换成 UI 上的组件,那Service上的也可以做到这一点。
从 api/abp/api-definition 可以得到各个api parameter的meta data,不过默认返回 PropertyApiDescriptionModel 的内容没有包含长度之类。
需要进一步改造,由于 PropertyApiDescriptionModel 和 TypeApiDescriptionModel 都不是接口类,想另外加field就要起多个新的Api
MyAbpApiDefinitionController

下面代码用的是abp5的版本

[Area("abp")]
[RemoteService(Name = "abp")]
[Route("api/abp/abp-api-definition")]
public class MyAbpApiDefinitionController : AbpController, IRemoteService
{
    private readonly IMyApiDescriptionModelProvider _modelProvider;
    private readonly IDistributedCache<MyApplicationApiDescriptionModel> _cache;

    public MyAbpApiDefinitionController(IMyApiDescriptionModelProvider modelProvider
        , IDistributedCache<MyApplicationApiDescriptionModel> cache)
    {
        _modelProvider = modelProvider;
        _cache = cache;
    }

    //[HttpGet]
    //public ApplicationApiDescriptionModel Get(ApplicationApiDescriptionModelRequestDto model)
    //{
    //    return _modelProvider.CreateApiModel(model);
    //}

    [HttpGet]
    public MyApplicationApiDescriptionModel GetMyApplicationApiDescriptionModel(ApplicationApiDescriptionModelRequestDto model)
    {
        return  _cache.GetOrAdd(
            "CacheApplicationApiDescriptionModel", //Cache key
            () => GetCacheApplicationApiDescriptionModel(model),
            () => new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddHours(24)
            }
        );
        
    }

    protected MyApplicationApiDescriptionModel GetCacheApplicationApiDescriptionModel(ApplicationApiDescriptionModelRequestDto model)
    {
        return _modelProvider.CreateMyApiModel(model);
    }
}

IMyApiDescriptionModelProvider

public interface IMyApiDescriptionModelProvider
{
    ApplicationApiDescriptionModel CreateApiModel(ApplicationApiDescriptionModelRequestDto input);

    MyApplicationApiDescriptionModel CreateMyApiModel(ApplicationApiDescriptionModelRequestDto input);
}

MyApplicationApiDescriptionModel

可以只返回customer的service

MyAspNetCoreApiDescriptionModelProvider

public class MyAspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvider, ITransientDependency
{
    public ILogger<AspNetCoreApiDescriptionModelProvider> Logger { get; set; }

    private readonly AspNetCoreApiDescriptionModelProviderOptions _options;
    private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider;
    private readonly AbpAspNetCoreMvcOptions _abpAspNetCoreMvcOptions;
    private readonly AbpApiDescriptionModelOptions _modelOptions;

    public MyAspNetCoreApiDescriptionModelProvider(
        IOptions<AspNetCoreApiDescriptionModelProviderOptions> options,
        IApiDescriptionGroupCollectionProvider descriptionProvider,
        IOptions<AbpAspNetCoreMvcOptions> abpAspNetCoreMvcOptions,
        IOptions<AbpApiDescriptionModelOptions> modelOptions)
    {
        _options = options.Value;
        _descriptionProvider = descriptionProvider;
        _abpAspNetCoreMvcOptions = abpAspNetCoreMvcOptions.Value;
        _modelOptions = modelOptions.Value;

        Logger = NullLogger<AspNetCoreApiDescriptionModelProvider>.Instance;
    }

    public MyApplicationApiDescriptionModel CreateMyApiModel(ApplicationApiDescriptionModelRequestDto input)
    {
        //TODO: Can cache the model?

        var model = MyApplicationApiDescriptionModel.Create();
        var apiItems = _descriptionProvider.ApiDescriptionGroups.Items.Where(o => o.Items.Any(p => p.RelativePath.StartsWith("api/app"))).ToList();
        foreach (var descriptionGroupItem in apiItems)
        {
            foreach (var apiDescription in descriptionGroupItem.Items)
            {
                if (!apiDescription.ActionDescriptor.IsControllerAction())
                {
                    continue;
                }

                AddApiDescriptionToModel(apiDescription, model, input);
            }
        }

        return model;
    }

MyPropertyApiDescriptionModel

[Serializable]
public class MyPropertyApiDescriptionModel 
{

    public string Name { get; set; }

    public string JsonName { get; set; }

    public string Type { get; set; }

    public string TypeSimple { get; set; }

    public bool IsRequired { get; set; }

    public string DisplayName { get; set; }

    public int Min { get; set; }

    public int Max { get; set; }


    //TODO: Validation rules for this property
    public static MyPropertyApiDescriptionModel Create(PropertyInfo propertyInfo)
    {
        MyPropertyApiDescriptionModel propertyModel =  new MyPropertyApiDescriptionModel
        {
            Name = propertyInfo.Name,
            JsonName = AbpApiProxyScriptingConfiguration.PropertyNameGenerator.Invoke(propertyInfo),
            Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType),
            TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType),
            IsRequired = propertyInfo.IsDefined(typeof(RequiredAttribute), true)
        };
        var attributes = propertyInfo.GetCustomAttributes(true);
        if (propertyInfo.IsDefined(typeof(StringLengthAttribute), true))
        {
            var attr = attributes.OfType<StringLengthAttribute>().FirstOrDefault();
            propertyModel.Min = attr.MinimumLength;
            propertyModel.Max = attr.MaximumLength;
        }
        if (propertyInfo.IsDefined(typeof(RangeAttribute), true))
        {
            var attr = attributes.OfType<RangeAttribute>().FirstOrDefault();
            propertyModel.Min = Convert.ToInt32(attr.Minimum);
            propertyModel.Max = Convert.ToInt32(attr.Maximum);
        }
        if (!propertyInfo.PropertyType.IsEnum && propertyInfo.IsDefined(typeof(DisplayAttribute), true))
        {
            var attr = attributes.OfType<DisplayAttribute>().FirstOrDefault();
            propertyModel.DisplayName = attr.GetName();
        }
        else
            propertyModel.DisplayName = propertyModel.Name;


        return propertyModel;
    }
}

MyTypeApiDescriptionModel

[Serializable]
public class MyTypeApiDescriptionModel
{
    public string BaseType { get; set; }

    public bool IsEnum { get; set; }

    //public string[] EnumNames { get; set; }

    //public object[] EnumValues { get; set; }

    public string[] GenericArguments { get; set; }

    public MyPropertyApiDescriptionModel[] Properties { get; set; }

    public MySelectionStringValueItem[] EnumProperties { get; set; }

    public MyTypeApiDescriptionModel()
    {

    }

    public static MyTypeApiDescriptionModel Create(Type type)
    {
        var baseType = type.BaseType;
        if (baseType == typeof(object))
        {
            baseType = null;
        }

        var typeModel = new MyTypeApiDescriptionModel
        {
            IsEnum = type.IsEnum,
            BaseType = baseType != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(baseType) : null
        };



        if (typeModel.IsEnum)
        {
            //typeModel.EnumNames = type.GetEnumNames();
            //typeModel.EnumValues = type.GetEnumValues().Cast<object>().ToArray();
            typeModel.EnumProperties = EnumHelper.ConvertEnumToArray(type);
        }
        else
        {
            typeModel.Properties = type
                .GetProperties()
                .Where(p => p.DeclaringType == type)
                .Select(MyPropertyApiDescriptionModel.Create)
                .ToArray();

            if (type.IsGenericTypeDefinition)
            {
                typeModel.GenericArguments = type.GetGenericArguments().Select(a => a.Name).ToArray();
            }
        }


        return typeModel;
    }
}

ProductDto

    public class ProductDto : FullAuditedEntityDto<Guid>
    {
        [Required]
        [Display(Name = "Product Name")]
        [StringLength(50, MinimumLength = 5,ErrorMessage = "Invalid Product Name.")]
        public string Name { get; set; }
        
        [JsonProperty("testvalue")]
        public Guid StoreId { get; set; }

        [Range(0, 15, ErrorMessage = "Can only be between 0 .. 15")]
        public int Quantity { get; set; }

        public ProductType ProductType { get; set; }
    }
    
    
    public enum ProductType
    {
        [Display(Name = "Product Type 1")]
        ProductType1 = 0,
        ProductType2 = 1,
        ProductType3 = 2,
    }

调用 api/abp/abp-api-definition 结果如下,这样返回的内容就可以给UI进行validation了

 

 

 

posted on 2022-03-24 23:00  白马酒凉  阅读(239)  评论(0编辑  收藏  举报

导航