我正在调用WebAPI的方法,该方法发送一个我想与模型匹配(或绑定)的json。
在控制器中,我有一个类似的方法:
public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model);
“ MyClass”,作为一个抽象类的参数给出。我想根据所传递的json的类型,实例化正确的继承类。
为了实现它,我正在尝试实现一个自定义的活页夹。问题是(我不知道它是否很基本,但是什么也找不到)我不知道如何检索请求中包含的原始Json(或更好的某种序列化)。
我知道了:
但是所有方法都公开为异步方法。我不知道这适合将生成模型传递给控制器方法的人…
非常感谢!
您不需要自定义模型活页夹。您也不需要考虑请求管道。
看看另一个SO:如何在JSON.NET中实现自定义JsonConverter以反序列化基类对象列表?。
我以此为基础来解决自己的问题。
从JsonCreationConverter<T>该SO中的引用开始(经过稍微修改以解决响应中类型序列化的问题):
JsonCreationConverter<T>
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指向自定义转换器:
JsonConverterAttribute
[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,但是如果我们发布:
arg
DerivedClass
{ DefaultProperty: 'world' }
然后,您将获得的实例DefaultClass。
DefaultClass
TypeNameHandling.Auto/All
我确实相信使用TypeNameHandling.Auto/AllJotaBe支持的方法并非总是理想的解决方案。在这种情况下很可能会- 但是我个人不会这样做,除非:
使用Json.Net TypeNameHandling.Auto或时All,您的Web服务器将开始以格式发送类型名称MyNamespace.MyType, MyAssemblyName。
TypeNameHandling.Auto
All
MyNamespace.MyType, MyAssemblyName
我在评论中说过,我认为这是一个安全问题。我从Microsoft阅读的一些文档中提到了这一点。似乎没有再提及,但是我仍然觉得这是一个合理的问题。我 从来 不想向外界公开名称空间限定的类型名称和程序集名称。它增加了我的攻击面。因此,是的,我无法拥有ObjectAPI类型的属性/参数,但是谁能说我网站的其余部分完全没有漏洞呢?谁说未来的端点不会暴露利用类型名称的能力?为什么仅仅因为它更容易而抓住这次机会?
Object
另外-如果您正在编写“适当的” API,即专门为第三方使用而不仅仅是为自己使用,并且正在使用Web API,那么您很可能希望利用JSON / XML内容类型处理(至少)。了解您尝试编写易于使用的文档的程度,该文档针对XML和JSON格式对所有API类型的引用有所不同。
通过重写JSON.Net如何理解类型名称,您可以将二者结合在一起,纯粹根据喜好在调用方的XML / JSON之间进行选择,而不是因为类型名称更容易在一个或另一个中记住。