2016-07-12 8 views
3

Ich habe ein XDocument in einem Controller, den ich als xml und json Server (abhängig von der Accept-Header der Anfrage) Server möchten.Rückgabe von XDocument von Controller (dotnet corclr)

Ich bin mit Dotnet Kern:

In meinem startup.cs/ConfigureServices Ich habe dies:

services.AddMvc().AddXmlDataContractSerializerFormatters(); 

Mein Controller im Wesentlichen ist dies:

public async Task<IActionResult> getData(int id) 
{ 
    XDocument xmlDoc = db.getData(id); 
    return Ok(xmlDoc); 
} 

Wenn eine Anfrage machen mit Accept: application/json, ich bekomme meine Daten richtig als JSON formatiert. Wenn ich eine Anfrage mit Accept: application/xml mache, bekomme ich immer noch eine JSON-Antwort (wie bei application/json).

Ich habe auch versucht, mit:

services.AddMvc().AddXmlSerializerFormatters(); 

aber das war noch schlimmer als auch normale Objekte als JSON bedient wurden (XmlDataContractSerializer normale Objekte umgehen kann, aber nicht XDocument).

Wenn ich den Controller [Produces("application/xml")] hinzufügen (AddXmlSerializerFormatters), bekomme ich einen Http 406 Fehler beim Serving XDocument, aber ich bekomme eine XML-Ausgabe bei der Rückgabe von normalen Objekten.

Muss ich das XDocument in Objekte konvertieren, um XML von einem Controller auszugeben? Gibt es eine einfache Möglichkeit, XDocuments in Objekte zu konvertieren?

Antwort

1

löste ich das Problem für die XmlDataContractSerializerOutputFormatter den Quellcode verwendet und ersetzt WriteResponseBodyAsync mit diesem (5 Zeilen mit Kommentar hinzugefügt):

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(); 
     } 
    } 

Ich bin nicht ganz mit dieser Lösung zufrieden, aber es macht die Auszeichnung erlauben der Accept Header und produziert entweder JSON oder XML, wenn eine XDocument gegeben. Wenn das XDocument im Objekt ist, wird es nicht abgefangen. Das würde bedeuten, DataContractSerializer neu zu schreiben, etwas, das ich lieber nicht tun würde.

Merkwürdige ist, dass auf Microsofts eigene Dokumentation DataContractSerializer der Lage sein sollten XDocument zu handhaben:

https://msdn.microsoft.com/en-us/library/ms731923(v=vs.110).aspx

3

Ich war in der Lage, das beschriebene Problem zu reproduzieren und nach dem Lesen einiger Quellcode im ASP.NET Core GitHub Repository (https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNetCore.Mvc.Formatters.Xml) gibt es eine fehlende Funktion im Xml Formatierer Projekt. Während der JSON-Formatierer XDocument-Werte erstaunlich gut verarbeitet, versucht der XML-Formatierer die XDocument-Instanz zu serialisieren, obwohl nicht alle Objekte serialisierbar sind. Wenn Sie XmlSerializerOutputFormatter durch die XmlData leiten lassen (indem Sie einfach die Zeichenfolgendarstellung im Stream schreiben), wird die Grundursache behoben.

Daher ist eine schnelle und relativ einfache/naive Abhilfe ist eine einfache Content zurückzukehren (wenn Content Negotiation keine strenge Anforderung ist), wie

return new ContentResult 
      { 
       Content = xmlDoc.ToString(), 
       ContentType = "text/xml", 
       StatusCode = 200 
      }; 

statt

return Ok(xmlDoc); 

Um zu löse die Ursache Ich empfehle eine Feature-Anfrage in der https://github.com/aspnet/Mvc Repo.

+0

Der Antrag wahrscheinlich hier eingereicht werden stattdessen sollte: https://github.com/dotnet/corefx als MVC verwendet nur die Serializer im Backend und hängt davon ab, ob sie einen Typ schreiben können oder nicht. Recht? –

+0

Ihre Lösung scheint zu funktionieren, danke, aber ich hatte gehofft, sowohl json als auch xml Ergebnis anbieten zu können. Ich habe zahlreiche Controller und zahlreiche Endpunkte in jedem Controller, der xml/json basierend auf XDocument anbieten muss. Manuelles Überprüfen der Accept-Header in jedem Controller/Endpunkt ist ein bisschen viel. Ich werde mir den Xml-Ausgabeformatierer ansehen – galmok