[英]Returning XDocument from Controller (dotnet coreclr)
我在控制器中有一個 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? 有沒有一種簡單的方法可以將 XDocument 轉換為對象?
在閱讀了 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 Repo 中提出一個功能請求。
我使用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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.