簡體   English   中英

從控制器返回 XDocument (dotnet coreclr)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM