c# - 在 Asp.Net Web API 中将 JSON 反序列化为派生类型

标签 c# asp.net-mvc asp.net-web-api

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

在 Controller 中我有这样的方法:

public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model);

作为参数给出的'MyClass'是一个抽象类。我希望根据传递的 json 类型,实例化正确的继承类。

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

我明白了:

  • actionContext.Request.Content

但是所有的方法都暴露为异步的。我不知道这适合谁将生成模型传递给 Controller ​​方法...

最佳答案

您不需要自定义模型 Binder 。您也不需要处理请求管道。

看看这个其他的 SO:How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects? .

我以此作为我自己解决同一问题的基础。

JsonCreationConverter<T> 开始在该 SO 中引用(略微修改以解决响应中类型序列化的问题):

public abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// this is very important, otherwise serialization breaks!
    /// </summary>
    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }
    /// <summary> 
    /// Create an instance of objectType, based properties in the JSON object 
    /// </summary> 
    /// <param name="objectType">type of object expected</param> 
    /// <param name="jObject">contents of JSON object that will be 
    /// deserialized</param> 
    /// <returns></returns> 
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType,
      object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        // Load JObject from stream 
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject 
        T target = Create(objectType, jObject);

        // Populate the object properties 
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }

    public override void WriteJson(JsonWriter writer, object value, 
      JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
} 

现在您可以使用 JsonConverterAttribute 注释您的类型,将 Json.Net 指向自定义转换器:

[JsonConverter(typeof(MyCustomConverter))]
public abstract class BaseClass{
  private class MyCustomConverter : JsonCreationConverter<BaseClass>
  {
     protected override BaseClass Create(Type objectType, 
       Newtonsoft.Json.Linq.JObject jObject)
     {
       //TODO: read the raw JSON object through jObject to identify the type
       //e.g. here I'm reading a 'typename' property:

       if("DerivedType".Equals(jObject.Value<string>("typename")))
       {
         return new DerivedClass();
       }
       return new DefaultClass();

       //now the base class' code will populate the returned object.
     }
  }
}

public class DerivedClass : BaseClass {
  public string DerivedProperty { get; set; }
}

public class DefaultClass : BaseClass {
  public string DefaultProperty { get; set; }
}

现在您可以使用基本类型作为参数:

public Result Post(BaseClass arg) {

}

如果我们要发布:

{ typename: 'DerivedType', DerivedProperty: 'hello' }

然后arg将是 DerivedClass 的一个实例,但如果我们发布:

{ DefaultProperty: 'world' }

然后你会得到一个 DefaultClass 的实例.

编辑 - 为什么我更喜欢这种方法而不是 TypeNameHandling.Auto/All

我相信使用 TypeNameHandling.Auto/All JotaBe 支持的并不总是理想的解决方案。在这种情况下很可能 - 但我个人不会这样做,除非:

  • 我或我的团队永远使用我的 API
  • 我不关心是否有一个双重 XML 兼容端点

当Json.Net TypeNameHandling.AutoAll被使用时,您的 Web 服务器将开始以 MyNamespace.MyType, MyAssemblyName 格式发送类型名称.

我在评论中说过,我认为这是一个安全问题。在我从微软读到的一些文档中提到了这一点。它似乎不再被提及,但我仍然觉得这是一个合理的担忧。我永远不想向外界公开命名空间限定的类型名称和程序集名称。它增加了我的攻击面。所以,是的,我不能Object属性/参数我的 API 类型,但谁能说我网站的其余部分完全没有漏洞?谁能说 future 的端点不会暴露利用类型名称的能力?为什么要捕获这个机会,因为它更容易?

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

通过重写 JSON.Net 理解类型名称的方式,您可以将两者结合起来,让调用者完全根据喜好在 XML/JSON 之间进行选择,而不是因为类型名称在一个或一个中更容易记住另一个。

关于c# - 在 Asp.Net Web API 中将 JSON 反序列化为派生类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12638741/

相关文章:

c# - 多次等待后, Controller 中的 HTTPContext.Current 为空

c# - 有没有办法在不按下 Ctrl 键的情况下在 ObjectListView 中进行多选?

ASP.Net MVC Controller 在部署到 IIS 7 时导致“未找到”错误

ASP.NET MVC 状态管理

jquery - Angularjs + ASP.NET MVC + ASP.NET Web API

c# - 在 Windows Phone 8.1 中设置锁屏图像

c# - 无法将类型为 'System.Security.Principal.GenericIdentity' 的对象转换为类型 'System.Web.Security.FormsIdentity'

jQuery 是与 Web 表单还是 HTML 表单一起使用?

c# - 通过 Web API 中的 ModelBinding 将 JavaScript 对象反序列化为 Dictionary<string, string>

asp.net-web-api - WebApi 2 从 Google 身份验证 OpenID 更改 -> oAuth2 - 更改安全句柄