C# 如何从继承的FromBody模型中获取正确的类型?
正文中有几个不同的XML收入。所有的XML几乎都是相同的,所以我首先添加了一个基类,其他的XML继承自基类 模型如下:C# 如何从继承的FromBody模型中获取正确的类型?,c#,asp.net-core,.net-core,C#,Asp.net Core,.net Core,正文中有几个不同的XML收入。所有的XML几乎都是相同的,所以我首先添加了一个基类,其他的XML继承自基类 模型如下: [XmlInclude(typeof(TextMsg))] [XmlRoot("xml")] public class BaseClass { public string ToUserName { get; set; } public string FromUserName { get; set; } public string Crea
[XmlInclude(typeof(TextMsg))]
[XmlRoot("xml")]
public class BaseClass
{
public string ToUserName { get; set; }
public string FromUserName { get; set; }
public string CreateTime { get; set; }
public string MsgType { get; set; }
}
[XmlRoot("xml")]
public class TextMsg : BaseClass
{
public TextMsg()
{
MsgType = "text";
}
public string Content { get; set; }
public string MsgId { get; set; }
}
还有几个类继承自基类模型,现在我只在这里展示一个
方法如下:
[HttpPost]
[Produces("application/xml")]
public async Task<IActionResult> mp([FromBody]BaseClass XmlData)
{
BaseClass ReturnXmlData = null;
var a = XmlData.GetType();
return Ok(ReturnXmlData);
}
您必须创建自定义modelbinder,这超出了堆栈溢出的范围。使用默认的modelbinder,参数的类型将被实例化,主体中任何不适合该类型的内容都将被丢弃。因此,您最终将得到一个
基类的实例,无法将其转换为任何更具体的派生类型
唯一的现成选项是直接绑定到所需的类类型(即派生类型,而不是基类型),并为每个派生类型指定备用端点。或者,您可以创建一个超类,该超类包含所有派生类型的所有属性,以便绑定到,然后根据填充的属性,您可以手动映射到相应的派生类。您可以根据其属性编写自己的XmlSerializerInputFormatter
,例如:
[Obsolete]
public class XmlCreationConverter: XmlSerializerInputFormatter
{
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context,
Encoding encoding)
{
XElement xml = null;
Type type = typeof(BaseClass);
var request = context.HttpContext.Request;
context.HttpContext.Request.EnableRewind();
using (StreamReader reader = new StreamReader(request.Body))
{
reader.BaseStream.Seek(0, SeekOrigin.Begin);
string text = reader.ReadToEnd();
request.Body.Position = 0; // rewind
xml = XElement.Parse(text);
//your logic to return the right type
if (xml.Element("MsgType").Value == "text")
{
type = typeof(TextMsg);
}
else if(...)
{
type = ...
}
else
{
}
using (XmlReader xmlReader = CreateXmlReader(request.Body, encoding))
{
var serializer = GetCachedSerializer(type);
var deserializedObject = serializer.Deserialize(xmlReader);
return InputFormatterResult.Success(deserializedObject);
}
}
}
}
行动:
[HttpPost]
[Produces("application/xml")]
public async Task<IActionResult> mp([FromBody]BaseClass XmlData)
可以为此创建自定义模型绑定器
public class XmlBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var memoryStream = new MemoryStream();
await bindingContext.HttpContext.Request.Body.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
XElement root = XElement.Load(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
string messageType = root.Element("MsgType").Value;
Type xmlType = GetTypeFromMessage(messageType);
var serializer = new XmlSerializer(xmlType);
var model = serializer.Deserialize(memoryStream);
bindingContext.Result = ModelBindingResult.Success(model);
}
catch
{
bindingContext.Result = ModelBindingResult.Failed();
}
}
private Type GetTypeFromMessage(string msgType)
{
switch (msgType)
{
case "text":
return typeof(TextMsg);
case "image":
return typeof(ImageMsg);
//... other cases
default:
return typeof(BaseClass);
}
}
}
将此型号活页夹应用于BaseClass
[ModelBinder(typeof(XmlBinder))]
public class BaseXml
传入的xml是否包含一些告诉我们类型的属性?或者我们只能通过xml中的属性计数/名称来理解类型是什么?请添加一些可能传入xml的示例好吗?@Alexander例如:123 456 1348831860文本这是一个测试1234567890123456@Alexander这里是另一个示例:123 456 1348831860图像这是一个url媒体id 1234567890123456有人告诉我使用多态模型绑定。正如你所看到的,我的模型非常简单,我认为我还不应该使用这些如此复杂的代码。这是一个简单的方法。创建自己的modelbinder来处理这一问题是很复杂的。最后一种方法是创建一个包含所有属性的超类。但是,我怀疑它是否会降低API的性能。我按照您的代码创建了一个名为XmlCreationConverter的新.cs文件,并添加了完整的代码。同时,VisualStudio报告了两个错误。一个是关于XmlCreationConverter类的“没有与所需的形式参数'options'相对应的参数'options'“'HttpRequest'不包含'EnableRewind'的定义,并且在“about”context.HttpContext.Request.EnableRewind()中找不到接受'HttpRequest'类型的第一个参数的可访问扩展方法'EnableRewind';“。我在谷歌上搜索过,有人说我应该安装一个Microsoft.AspNetCore.Http NuGet包来解决这个问题。我尝试过,但没有效果。很多人让我创建一个自定义modelbinder。我读过很多关于它的文章,但仍然很难了解它。看起来你的方式比自定义modelbinder的方式简单得多,也不同。你愿意吗?”请告诉我这两种方式的区别。谢谢。此外,我使用的.net core版本是.net core 3。0@102425074你需要有一个使用Microsoft.AspNetCore.Http.Internal的参考。我做的也是自定义绑定。哦,我在asp.net core 2.2中测试了它。唯一的缺点是matter将应用于所有可能与基类无关的xml请求。这不仅是模型绑定的最佳演示,也是我见过的最简单的方式!我非常喜欢它!我更喜欢这种方式,因为它只应用于一个类,而不是所有xml请求。正如我们所知,XM有很多意想不到的类型L未来项目的收入。这种方式更灵活。
public class XmlCreationConverter : XmlSerializerInputFormatter
{
private readonly MvcOptions _options;
public XmlCreationConverter(MvcOptions options) : base(options)
{
_options = options;
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context,
Encoding encoding)
{
XElement xml = null;
Type type = typeof(BaseClass);
var request = context.HttpContext.Request;
context.HttpContext.Request.EnableBuffering();
//for model type is Baseclass
if (context.ModelType == typeof(BaseClass))
{
using (StreamReader reader = new StreamReader(request.Body))
{
var text = await reader.ReadToEndAsync();
request.Body.Position = 0;
xml = XElement.Parse(text);
if (xml.Element("MsgType").Value == "text")
{
type = typeof(TextMsg);
}
using (XmlReader xmlReader = CreateXmlReader(request.Body, encoding))
{
var serializer = GetCachedSerializer(type);
var deserializedObject = serializer.Deserialize(xmlReader);
return InputFormatterResult.Success(deserializedObject);
}
}
}
else if(context.ModelType == ...)
else
{
using (StreamReader reader = new StreamReader(request.Body))
{
var text = await reader.ReadToEndAsync();
request.Body.Position = 0;
using (var xmlReader = CreateXmlReader(request.Body, encoding))
{
var modelType = GetSerializableType(context.ModelType);
var serializer = GetCachedSerializer(modelType);
var deserializedObject = serializer.Deserialize(xmlReader);
// Unwrap only if the original type was wrapped.
if (type != context.ModelType)
{
if (deserializedObject is IUnwrappable unwrappable)
{
deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType);
}
}
return InputFormatterResult.Success(deserializedObject);
}
}
}
}
}
services.AddMvc(options => options.InputFormatters.Add(new XmlCreationConverter(options)))
.AddXmlSerializerFormatters();
public class XmlBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var memoryStream = new MemoryStream();
await bindingContext.HttpContext.Request.Body.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
XElement root = XElement.Load(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
string messageType = root.Element("MsgType").Value;
Type xmlType = GetTypeFromMessage(messageType);
var serializer = new XmlSerializer(xmlType);
var model = serializer.Deserialize(memoryStream);
bindingContext.Result = ModelBindingResult.Success(model);
}
catch
{
bindingContext.Result = ModelBindingResult.Failed();
}
}
private Type GetTypeFromMessage(string msgType)
{
switch (msgType)
{
case "text":
return typeof(TextMsg);
case "image":
return typeof(ImageMsg);
//... other cases
default:
return typeof(BaseClass);
}
}
}
[ModelBinder(typeof(XmlBinder))]
public class BaseXml