我在控制器中有一个XDocument,我希望将其作为xml和json服务器(取决于请求的Accept标头)。
我使用的是dotnet核心:
在我的startup.cs / ConfigureServices中我有:
services.AddMvc().AddXmlDataContractSerializerFormatters();
我的控制器基本上是这样的:
public async Task<IActionResult> getData(int id)
{
XDocument xmlDoc = db.getData(id);
return Ok(xmlDoc);
}
使用Accept: application/json
发出请求时,我的数据格式正确为JSON格式。在使用Accept: application/xml
发出请求时,我仍然会收到JSON响应(与application/json
相同)。
我也尝试过:
services.AddMvc().AddXmlSerializerFormatters();
但更糟糕的是,甚至普通对象都被用作JSON(XmlDataContractSerializer可以处理普通对象,但不能处理XDocument)。
当我向控制器添加[Produces("application/xml")]
时(使用AddXmlSerializerFormatters
),在提供XDocument时出现Http 406错误,但在返回普通对象时确实得到了XML输出。
我是否必须将XDocument转换为对象以从控制器输出XML?有没有简单的方法将XDocuments转换为对象?
答案 0 :(得分:4)
我能够重现所描述的问题,在阅读了ASP.NET Core GitHub存储库(https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNetCore.Mvc.Formatters.Xml)中的一些源代码后,Xml格式化程序项目中缺少一项功能。虽然JSON格式化程序处理XDocument值非常好,但xml格式化程序尝试序列化XDocument实例,尽管并非所有对象都可序列化。 启用XmlSerializerOutputFormatter以通过XmlData(只需在流上写入字符串表示)将解决根本原因。
因此,快速而简单/天真的解决方法是返回一个简单的ContentResult(如果内容协商不是严格的要求),例如
return new ContentResult
{
Content = xmlDoc.ToString(),
ContentType = "text/xml",
StatusCode = 200
};
而不是
return Ok(xmlDoc);
为了解决根本原因,我建议在https://github.com/aspnet/Mvc回购中提出功能请求。
答案 1 :(得分:1)
我使用XmlDataContractSerializerOutputFormatter
的源代码解决了这个问题,并用此替换了WriteResponseBodyAsync
(包括添加了评论的5行):
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (selectedEncoding == null)
{
throw new ArgumentNullException(nameof(selectedEncoding));
}
var writerSettings = WriterSettings.Clone();
writerSettings.Encoding = selectedEncoding;
// Wrap the object only if there is a wrapping type.
var value = context.Object;
var wrappingType = GetSerializableType(context.ObjectType);
if (wrappingType != null && wrappingType != context.ObjectType)
{
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(new WrapperProviderContext(
declaredType: context.ObjectType,
isSerialization: true));
value = wrapperProvider.Wrap(value);
}
var dataContractSerializer = GetCachedSerializer(wrappingType);
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
{
using (var xmlWriter = CreateXmlWriter(textWriter, writerSettings))
{
// If XDocument, use its own serializer as DataContractSerializer cannot handle XDocuments.
if (value is XDocument)
{
((XDocument)value).WriteTo(xmlWriter);
}
else
dataContractSerializer.WriteObject(xmlWriter, value);
}
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
// buffers. This is better than just letting dispose handle it (which would result in a synchronous
// write).
await textWriter.FlushAsync();
}
}
我对此解决方案并不完全满意,但它确实允许尊重Accept
标头,并在给定XDocument
时生成JSON或XML。如果XDocument
在对象内部,则不会被捕获。这意味着重写DataContractSerializer,我宁愿不这样做。
奇怪的是,在微软上,自己的文档DataContractSerializer
应该能够处理XDocument
:
https://msdn.microsoft.com/en-us/library/ms731923(v=vs.110).aspx