如何在api调用中传递多种类类型?

时间:2018-08-22 15:25:41

标签: c# asp.net-mvc asp.net-core asp.net-core-webapi

我正在尝试寻找一种方法,以通过接受基本类型的集合将多种类型传递到api put请求中。 我正在使用Asp.Net Core 2.1.0,我似乎找不到简单的方法来做到这一点?你知道这样做的方法吗?

    public abstract class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class GoodPerson :Person
    {
        public int NumberOfGoodDeeds { get; set; }
    }

    public class BadPerson : Person
    {
        public int NumberOfBadDeeds { get; set; }
    }

    [HttpPut]
    public IActionResult Update([FromBody]Collection<Person> persons)
    {
        //do stuff with concrete classes
        return Ok();
    }

我在api调用的正文中使用JSON传递了参数。

[  
{
    "Age": 12,
    "Name": "Bob",
    "NumberOfGoodDeeds": 2
},
{
    "Age": 18,
    "Name": "Andy",
    "NumberOfBadDeeds": 5
}
]

1 个答案:

答案 0 :(得分:1)

您不能。操作参数与普通方法参数不同。例如,使用普通方法,您将实例化GoodPerson的副本并将其传递给上游,并将其复制到Person。然后,在方法代码中,您可以使用模式匹配,强制转换等方式返回“真实”类型。之所以有效,是因为最终它在内存中是GoodPerson

但是,使用操作方法,您所拥有的只是一些字符串或一些表示类的JSON。为了激活动作,模型绑定器检查动作方法的参数类型,然后尝试初始化该类型的实例并将数据映射到该实例上。 (就JSON而言,从技术上讲,这是一个反序列化过程,但重要的是,它仍将尝试反序列化为param类型。)任何多余的数据都将被丢弃。结果,在您的action方法内部,Person实例实际上是<{>} Person实例。永远不会是GoodPersonBadPerson

您在这里可以做的最好的事情是创建一个包含所有可能的类及其所有属性的类。例如:

public class PersonViewModel
{
    public string Name { get; set; }
    public int Age { get; set; }

    // Note the use of nullables here
    public int? NumberOfGoodDeeds { get; set; }
    public int? NumberOfBadDeeds { get; set; }
} 

然后,您绑定到该视图模型类,而不是Person。在您的操作内部,您可以根据类型之间的已知差异进行分支,并将数据从视图模型映射到正确的类类型:

Person person;

if (model.NumberOfGoodDeeds.HasValue)
{
    person = new GoodPerson { NumberOfGoodDeeds = model.NumberOfGoodDeeds.Value };
}
else if (model.NumberOfBadDeeds.HasValue)
{
    person = new BadPerson { NumberOfBadDeeds = model.NumberOfBadDeeds.Value };
}
else
{
    person = new Person();
}

person.Name = model.Name;
person.Age = model.Age;

使用像AutoMapper这样的映射库可以帮助最小化其中的一些代码,但是您可以了解基本思想。现在,从本质上讲,您本来可以直接在内存中调用带有实例的方法。因此,如果您需要对特定类型进行操作,则只需使用模式匹配之类的方法即可:

switch (person)
{
    case GoodPerson goodPerson:
        // do something with `goodPerson`
        break;
    case BadPerson badPerson:
        // do something with `badPerson`
        break;
}