简体   繁体   English

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

[英]Using inherited classes in .NET web API POST/PUT method

I can't figure out how I can work with inherited classes in an web API controller. 我不知道如何在Web API控制器中处理继承的类。 I have to create only one API controller to create and update inherited objects in the database. 只需要创建一个 API控制器即可创建和更新数据库中的继承对象。

Similar to my models (to all these models exists a Dto): 类似于我的模型(所有这些模型都存在一个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;}
}

I already tried to use the json in the controller. 我已经尝试在控制器中使用json。 But there was the json always null 但是json总是为null

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

My controller now, but that cuts off all attributes of Dog or Cat model 现在是我的控制器,但这切断了Dog或Cat模型的所有属性

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

I'm using .NET Core 2.0 我正在使用.NET Core 2.0

Any ideas? 有任何想法吗? Thanks! 谢谢!

EDIT 编辑

What if I want to do this dynamically? 如果我想动态地执行此操作怎么办? Something like: 就像是:

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

How can I do this? 我怎样才能做到这一点?

You haven't mentioned that your are using either .NET Core or .NET Framework, but the main reason of this kind of behavior is model binding module which maps data from HTTP requests to action method parameters. 您没有提到您正在使用.NET Core或.NET Framework,但是这种行为的主要原因是模型绑定模块,该模块将数据从HTTP请求映射到操作方法参数。 I think you'd better to develop your own custom model binder and use it instead of the default one. 我认为您最好开发自己的自定义模型活页夹,并使用它代替默认的活页夹。

Here you can read more about custom model binding in asp .NET Core 在这里,您可以阅读有关ASP .NET Core中的自定义模型绑定的更多信息。

Here you can read more about custom model binding in asp .NET MVC 在这里,您可以阅读有关ASP .NET MVC中的自定义模型绑定的更多信息。

I already tried to use the json in the controller. 我已经尝试在控制器中使用json。 But there was the json always null 但是json总是为null

The reason why you always get null is that you didn't declare the correct Type . 总是得到null的原因是您没有声明正确的Type You're not supposed to receive a string type of animal via [FromBody] . 您不应该通过[FromBody]接收string类型的动物。

[HttpPost]
    
 

 
 
 
  
  
  
 
  public ActionResult Post([FromBody]string json)
 

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

As a side note, if you don't care the type at all, one approach is to declare a dynamic type: 附带说明一下,如果您根本不关心类型,则一种方法是声明dynamic类型:

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

In this scenario, the dynamic type is JObject , you could cast them to any type as you like: 在这种情况下,动态类型是JObject ,您可以根据需要将它们JObject为任何类型:

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

My controller now, but that cuts off all attributes of Dog or Cat model 现在是我的控制器,但这切断了Dog或Cat模型的所有属性

If you would like to use the AnimalDto , you should make the properties accessible: 如果要使用AnimalDto ,则应使属性可访问:

public class AnimalDto
    {
        
 

 
 
 
  
  
  
 
  public string Name;
 

 
 
 
        public string Name{get;set;}
        
 

 
 
 
  
  
  
 
  public string Type;
 

 
 
 
        public string Type{get;set;}
    }

[Edit] [编辑]

What if I want to do this dynamically? 如果我想动态地执行此操作怎么办? Something like: 就像是:

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

If we want to cast the json to some particular type, we must firstly know what the target Type is . 如果我们想将json转换为某种特定类型,我们必须首先知道目标Type是什么。 But your animal.Type property is a type of String , and that might be a by-convention string. 但是您的animal.Type属性是String的类型,并且可能是常规字符串。 If this Type property is exactly the class name without namespace , eg 如果此Type属性恰好是没有名称空间的类名,例如

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

then you can you use Type.GetType() to resolve the target type. 那么您可以使用Type.GetType()解析目标类型。 For example : 例如 :

// 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;
}

Anyway, you could custom the way we resolve the target Type according to your own needs . 无论如何, 您可以根据自己的需要自定义我们解析目标Type的方式 And then cast the json object to your wanted Type by method ToObject<T>() : 然后通过方法ToObject<T>()将json对象转换为所需的Type

[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); 
}

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

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