在.NET Web API POST / PUT方法中使用继承的类

时间:2019-01-12 10:33:32

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

我不知道如何在Web API控制器中处理继承的类。我必须创建仅一个 API控制器以创建和更新数据库中的继承对象。

类似于我的模型(所有这些模型都具有一个Dto):

public class Animal
{
    public virtual string Name {get; set;} // e.g. Harry
    public virtual string Type {get; set;} // e.g. Dog
}

public class AnimalDto
{
    public string Name;
    public Type Type;
}

public class Dog : Animal
{
    public virtual bool CanBark {get; set;} // e.g. true
}

public class Cat : Animal
{
    public virtual bool CanMiau {get; set;}
}

我已经尝试在控制器中使用json。但是总是有json null

[HttpPost]
public ActionResult Post([FromBody]JObject json)
{
    // idk what's going here?!
}

现在是我的控制器,但这切断了Dog或Cat模型的所有属性

[HttpPost]
public ActionResult Post([FromBody]AnimalDto animal)
{
    // idk what's going here?!
}

我正在使用.NET Core 2.0

有什么想法吗?谢谢!

编辑

如果我想动态地执行此操作怎么办?像这样:

var animal = json.ToObject<Animal>();
var actualAnimal = json.ToObject<typeof(animal.Type)>();

我该怎么做?

2 个答案:

答案 0 :(得分:0)

您没有提到您正在使用.NET Core或.NET Framework,但是这种行为的主要原因是模型绑定模块,该模块将数据从HTTP请求映射到操作方法参数。我认为您最好开发自己的自定义模型活页夹,并使用它代替默认的活页夹。

Here you can read more about custom model binding in asp .NET Core

Here you can read more about custom model binding in asp .NET MVC

答案 1 :(得分:0)

  

我已经尝试在控制器中使用json。但是json总是为空

总是得到null的原因是您没有声明正确的Type。您不应该通过string来获得[FromBody]类动物。

    [HttpPost]
    public ActionResult Post([FromBody]string json)
    public ActionResult Post([FromBody]Dog dog)
    {
        // now you get the dog
    }

请注意,如果您根本不关心类型,则一种方法是声明dynamic类型:

[HttpPost]
public IActionResult Post([FromBody] dynamic json)
{
    return new JsonResult(json); 
}

在这种情况下,动态类型为JObject,您可以将它们强制转换为任意类型:

public IActionResult Post(/*[ModelBinder(typeof(AnimalModelBinder))]*/[FromBody] JObject json)
{
    var animal=json.ToObject<Animal>();
    var dog = json.ToObject<Dog>();
    return new JsonResult(json); 
}
  

现在是我的控制器,但这切断了Dog或Cat模型的所有属性

如果您想使用AnimalDto,则应使属性可访问:

    public class AnimalDto
    {
        public string Name;
        public string Name{get;set;}
        public string Type;
        public string Type{get;set;}
    }

[编辑]

  

如果我想动态地执行此操作怎么办?像这样:

var animal = json.ToObject<Animal>();
var actualAnimal = json.ToObject<typeof(animal.Type)>();

如果我们想将json转换为某种特定类型,我们首先必须知道目标Type是什么。但是您的animal.Type属性是String的类型,它可能是约定俗成的字符串。如果此Type属性恰好是没有名称空间的类名,例如

  • Cat.Type必须等于Cat
  • Dog.Type必须等于Dog
  • Fish.Type必须等于Fish

然后您可以使用Type.GetType()来解析目标类型。例如:

// typename : the Animal.Type property
// ns : the namespace string 
private Type ResolveAnimalType(string typename,string ns){
    if(string.IsNullOrEmpty(ns)){ ns = "App.Models";}
    typename= $"{ns}.{typename}";
    var type=Type.GetType(
        typename,
        assemblyResolver:null,  // if you would like load from other assembly, custom this resovler
        typeResolver: (a,t,ignore)=> a == null ? 
            Type.GetType(t, false, ignore) : 
            a.GetType(t, false, ignore),
        throwOnError:true,
        ignoreCase:false
    );
    return type;
}

无论如何,您可以根据自己的需求自定义我们解决目标Type的方式。然后通过方法Type 将json对象投射到所需的ToObject<T>()

[HttpPost]
public IActionResult Post([FromBody] JObject json)
{
    var typename = json.Value<string>("type");
    if(String.IsNullOrEmpty(typename)) 
        ModelState.AddModelError("json","any animal must provide a `type` property");

    // resolve the target type
    var t= ResolveAnimalType(typename,null);

    // get the method of `.ToObject<T>()`
    MethodInfo mi= typeof(JObject)
        .GetMethods(BindingFlags.Public|BindingFlags.Instance)
        .Where(m=> m.Name=="ToObject" && m.GetParameters().Length==0 && m.IsGenericMethod  )
        .FirstOrDefault()
        ?.MakeGenericMethod(t); // ToObject<UnknownAnimialType>()

    var animal = mi?.Invoke(json,null);
    return new JsonResult(animal); 
}