Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在Asp.NETWebAPI中将JSON反序列化为派生类型_C#_Asp.net Mvc_Asp.net Web Api - Fatal编程技术网

C# 在Asp.NETWebAPI中将JSON反序列化为派生类型

C# 在Asp.NETWebAPI中将JSON反序列化为派生类型,c#,asp.net-mvc,asp.net-web-api,C#,Asp.net Mvc,Asp.net Web Api,我正在调用WebAPI的一个方法,发送一个我希望与模型匹配(或绑定)的JSON 在控制器中,我有如下方法: public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model); 作为参数提供的“MyClass”是一个抽象类。我希望根据传递的json类型,实例化正确的继承类 为了实现它,我正在尝试实现一个自定义绑定器。问题是(我不知道它是否非常基本,但我找不到任何东西)我不知道如何检索请求中的原始JSON(或者更好,

我正在调用WebAPI的一个方法,发送一个我希望与模型匹配(或绑定)的JSON

在控制器中,我有如下方法:

public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model);
作为参数提供的“MyClass”是一个抽象类。我希望根据传递的json类型,实例化正确的继承类

为了实现它,我正在尝试实现一个自定义绑定器。问题是(我不知道它是否非常基本,但我找不到任何东西)我不知道如何检索请求中的原始JSON(或者更好,某种序列化)

我明白了:

  • actionContext.Request.Content

但所有方法都以异步方式公开。我不知道将生成模型传递给控制器方法适合谁…

您可以正常调用异步方法,您的执行将被暂停,直到方法返回,并且您可以以标准方式返回模型。就这样打个电话吧:

string jsonContent = await actionContext.Request.Content.ReadAsStringAsync();

它将为您提供原始JSON。

您不需要自定义模型绑定器。你也不需要在请求管道上乱搞

看看另一个,所以:

我用这个作为我自己解决同一问题的基础

从中引用的
JsonCreationConverter
开始(稍作修改以修复响应中类型序列化的问题):

现在,可以将基类型用作参数:

public Result Post(BaseClass arg) {

}
如果我们要发布:

{ typename: 'DerivedType', DerivedProperty: 'hello' }
然后,
arg
将是
DerivedClass
的一个实例,但如果我们发布:

{ DefaultProperty: 'world' }
然后您将得到
DefaultClass
的一个实例

编辑-为什么我喜欢此方法而不是
typenameholling.Auto/All
我相信使用JotaBe支持的
TypeNameHandling.Auto/All
并不总是理想的解决方案。在这种情况下很可能是这样,但就我个人而言,除非:

  • 我的API只供我或我的团队使用
  • 我不在乎有一个双XML兼容的端点
当使用Json.Net
typenameholling.Auto
All
时,web服务器将开始以
MyNamespace.MyType、MyAssemblyName
的格式发送类型名称

我在评论中说过,我认为这是一个安全问题。在我从微软读到的一些文档中提到了这一点。它似乎不再被提及,但我仍然觉得这是一个合理的担忧。我不想向外界公开命名空间限定的类型名和程序集名。这增加了我的攻击面。所以,是的,我不能让
Object
properties/parameters作为我的API类型,但是谁能说我的站点的其余部分完全没有漏洞呢?谁说未来的端点不会暴露利用类型名的能力?为什么仅仅因为比较容易就要冒这个险呢

另外,如果您正在编写一个“适当的”API,即专门供第三方使用,而不仅仅是供您自己使用,并且您正在使用Web API,那么您最有可能希望利用JSON/XML内容类型处理(至少)。看看您在尝试编写易于使用的文档方面取得了多大进展,这些文档以不同的方式引用XML和JSON格式的所有API类型


通过覆盖JSON.Net理解类型名的方式,您可以将两者结合起来,使调用方在XML/JSON之间的选择完全基于品味,而不是因为类型名在其中一个中更容易记住。

您不需要自己实现它。JSON.NET对它有本机支持

您必须为JSON格式化程序指定以下内容(在
global.asax
application start事件中):

如果指定了
Auto
,就像上面的示例一样,参数将反序列化为对象的
$type
属性中指定的类型。如果缺少
$type
属性,它将被反序列化为参数的类型。因此,在传递派生类型的参数时,只需指定类型。(这是最灵活的选择)

例如,如果将此参数传递给Web API操作:

var param = {
    $type: 'MyNamespace.MyType, MyAssemblyName', // .NET fully qualified name
    ... // object properties
};
参数将反序列化为
MyNamespace.MyType
class的对象

这也适用于子属性,也就是说,您可以有这样一个对象,它指定内部属性是给定类型的

var param = { 
   myTypedProperty: {
      $type: `...`
      ...
};
在这里你可以看到一张照片

注意

你不需要用阁楼装饰任何东西,也不需要做任何其他定制。它将在不更改Web API代码的情况下工作

重要提示

。如果没有,它将被忽略

与自定义JsonConverter/JsonConverterAttribute的比较

我正在比较本机解决方案

要实现
JsonConverter
/
JsonConverterAttribute

  • 您需要实现一个自定义的
    JsonConverter
    ,以及一个自定义的
    JsonConverterAttribute
  • 您需要使用属性来标记参数
  • 您需要事先知道参数的可能类型
  • 每当类型或属性发生更改时,您需要实现或更改
    JsonConverter
    的实现
  • 有一个代码气味,指示预期的属性名称
  • 您没有实现可用于任何类型的泛型
  • 你在重新发明轮子
在答案的作者中有一条关于安全性的评论。除非您做了一些错误的事情(比如为您的参数接受一个过于泛型的类型,比如
对象
),否则不会有获取错误类型实例的风险:JSON.NET本机解决方案仅实例化参数类型的对象,或从参数类型派生的类型(如果不是,则获取
null

这些是
JsonSerializerSettings serializerSettings = GlobalConfiguration.Configuration
   .Formatters.JsonFormatter.SerializerSettings;
serializerSettings.TypeNameHandling = TypeNameHandling.Auto;
var param = {
    $type: 'MyNamespace.MyType, MyAssemblyName', // .NET fully qualified name
    ... // object properties
};
var param = { 
   myTypedProperty: {
      $type: `...`
      ...
};
public class InheritanceSerializationBinder : DefaultSerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        switch (typeName)
        {
            case "parent[]": return typeof(Class1[]);
            case "parent": return typeof(Class1);
            case "child[]": return typeof(Class2[]);
            case "child": return typeof(Class2);
            default: return base.BindToType(assemblyName, typeName);
        }
    }
}
var config = GlobalConfiguration.Configuration;
        config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings { Binder = new InheritanceSerializationBinder() };