简体   繁体   English

具有通用参数的 ASP.NET Core Web API 控制器

[英]ASP.NET Core Web API controller with generic params

I have an ASP.NET Core Web API communicating with a flutter mobile app.我有一个与颤振移动应用程序通信的 ASP.NET Core Web API。

The feature I am adding is a notification service.我要添加的功能是通知服务。 The issue is I have more than one notification type.问题是我有不止一种通知类型。

Here is the code:这是代码:

public class NotificationSuper 
{
    public string  Title { get; set; }
    public string Body { get; set; }
    public string Token { get; set; }
    public string Type { get; set; }
}

public class UnitNotification :NotificationSuper
{
    public String Renter_Key { get; set; }
    public String Owner_Key { get; set; }
    public String Building_Key { get; set; }
    public String Unit_Key { get; set; }
}

public class MaintenanceNotification : UnitNotification
{
    public DateTime RequestData { get; set; }    
}

and so on.等等。

I wrote a controller for the notification using a super generic type in its params我在其参数中使用超泛型类型为通知编写了一个控制器

[HttpPost]
public async Task<IActionResult> Post([FromBody] NotificationSuper notification)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    bool success = await Notify.Send(notification);

    if (success)
    {
        return Ok();
    }

    return StatusCode(500);            
}

The problem is when I retrieve the JSON data from the flutter app, I only get the properties of the NotificationSuper class which are:问题是当我从颤振应用程序中检索 JSON 数据时,我只得到NotificationSuper类的属性,它们是:

public String Renter_Key { get; set; }
public String Owner_Key { get; set; }
public String Building_Key { get; set; }

I want to have a flexible way to get every property if I passed UnitNotification or MaintenanceNotification .如果我通过了UnitNotificationMaintenanceNotification ,我希望有一种灵活的方式来获取每个属性。 Should I have multiple controllers, one for each type of notification?我应该有多个控制器,每种类型的通知一个吗?

Thanks in advance提前致谢

You can combine your UnitNotification and MaintenanceNotification您可以结合您的UnitNotificationMaintenanceNotification

It should look like this它应该看起来像这样

public class CombinedNotification
{
    public UnitNotification unitNotification { get; set; }
    public MaintenanceNotification maintenanceNotification{ get; set; }
}

Then your controller code should look like this:然后您的控制器代码应如下所示:

[HttpPost]
public async Task<IActionResult> Post([FromBody] CombinedNotification notification)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    bool success;

    if (notification.UnitNotification != null)
    {
         bool success = await Notify.Send(notification.UnitNotification);
    }

    if (notification.MaintenanceNotification != null)
    {
         bool success = await Notify.Send(notification.MaintenanceNotification);
    }

    if (success)
    {
        return Ok();
    }

    return StatusCode(500);            
}

The important thing is your post data now must be changed from: unitNotification to {"unitNotification": {}}重要的是您的帖子数据现在必须从: unitNotification更改为{"unitNotification": {}}

Here is a more flexible method, I use Polymorphic model binding , You can refer to this simple demo:这里有一个比较灵活的方法,我用的是Polymorphic model binding ,可以参考这个简单的demo:

Model模型

public abstract class Device
{
    public string Kind { get; set; }

    public string Name { get; set; }
}

public class Laptop : Device
{
    public string CPUIndex { get; set; }

    public string Price { get; set; }
}

public class SmartPhone : Device
{
    public string ScreenSize { get; set; }
}

Model Binding Code模型绑定代码

public class DeviceModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType != typeof(Device))
        {
            return null;
        }

        var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };

        var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
        foreach (var type in subclasses)
        {
            var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
            binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
        }

        return new DeviceModelBinder(binders);
    }
}

public class DeviceModelBinder : IModelBinder
{
    private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;

    public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
    {
        this.binders = binders;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Kind));
        var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;

        IModelBinder modelBinder;
        ModelMetadata modelMetadata;
        if (modelTypeValue == "Laptop")
        {
            (modelMetadata, modelBinder) = binders[typeof(Laptop)];
        }
        else if (modelTypeValue == "SmartPhone")
        {
            (modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
        }
        else
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
            bindingContext.ActionContext,
            bindingContext.ValueProvider,
            modelMetadata,
            bindingInfo: null,
            bindingContext.ModelName);

        await modelBinder.BindModelAsync(newBindingContext);
        bindingContext.Result = newBindingContext.Result;

        if (newBindingContext.Result.IsModelSet)
        {
            // Setting the ValidationState ensures properties on derived types are correctly 
            bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
            {
                Metadata = modelMetadata,
            };
        }
    }
}

Demo:演示:

在此处输入图像描述

Notice: This demo needs data from form , If you need data from request body , You need to change some code in DeviceModelBinder And parent class needs a property to specify the class name of the subclass.注意:这个demo需要来自form的数据,如果需要来自request body的数据,需要修改DeviceModelBinder中的一些代码,并且父类需要一个属性来指定子类的类名。

Refer to doc .请参阅文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM