Wie kann die XML-Dokumentation für Web-API eine Dokumentation enthalten, die über das Hauptprojekt hinausgeht?

102

Die Dokumentation zum Aktivieren der XmlDoc-Integration in Ihre Web-Api-Projekte behandelt anscheinend nur Situationen, in denen alle Ihre API-Typen Teil Ihres WebApi-Projekts sind. Insbesondere wird erläutert, wie Sie die XML-Dokumentation in App_Data/XmlDocument.xmleine Zeile in Ihrer Konfiguration umleiten und diese auskommentieren, in der diese Datei verwendet wird. Dies erlaubt implizit nur die Dokumentationsdatei eines Projekts.

In meinem Setup habe ich jedoch meine Anforderungs- und Antworttypen in einem gemeinsamen "Modelle" -Projekt definiert. Dies bedeutet, dass wenn ich einen Endpunkt definiert habe wie:

[Route("auth/openid/login")]
public async Task<AuthenticationResponse> Login(OpenIdLoginRequest request) { ... }

Wo OpenIdLoginRequestist in einem separaten C # -Projekt wie folgt definiert:

public class OpenIdLoginRequest
{
    /// <summary>
    /// Represents the OpenId provider that authenticated the user. (i.e. Facebook, Google, etc.)
    /// </summary>
    [Required]
    public string Provider { get; set; }

    ...
}

Trotz der XML-Anweisungen enthalten die Eigenschaften des requestParameters keine Dokumentation, wenn Sie die endpunktspezifische Hilfeseite (dh http://localhost/Help/Api/POST-auth-openid-login) anzeigen .

Wie kann ich dafür sorgen, dass Typen in Teilprojekten mit XML-Dokumentation in der Web-API-XML-Dokumentation auftauchen?

Kirk Woll
quelle

Antworten:

165

Es gibt keinen eingebauten Weg, um dies zu erreichen. Es sind jedoch nur wenige Schritte erforderlich:

  1. Aktivieren Sie die XML-Dokumentation für Ihr Teilprojekt (aus Projekteigenschaften / Build) wie für Ihr Web-API-Projekt. Leiten Sie es außer diesmal direkt an weiter, XmlDocument.xmldamit es im Stammordner Ihres Projekts generiert wird.

  2. Ändern Sie das Postbuild-Ereignis Ihres Web-API-Projekts, um diese XML-Datei in Ihren App_DataOrdner zu kopieren :

    copy "$(SolutionDir)SubProject\XmlDocument.xml" "$(ProjectDir)\App_Data\Subproject.xml"

    Wo Subproject.xmlsollte unabhängig von Ihrem Projekt den Namen umbenannt werden plus .xml.

  3. Öffnen Sie Areas\HelpPage\App_Start\HelpPageConfigals nächstes die folgende Zeile und suchen Sie sie:

    config.SetDocumentationProvider(new XmlDocumentationProvider(
        HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

    Dies ist die Zeile, die Sie anfangs nicht kommentiert haben, um die XML-Hilfedokumentation überhaupt zu aktivieren. Ersetzen Sie diese Zeile durch:

    config.SetDocumentationProvider(new XmlDocumentationProvider(
        HttpContext.Current.Server.MapPath("~/App_Data")));

    Dieser Schritt stellt sicher, dass XmlDocumentationProviderdas Verzeichnis übergeben wird, das Ihre XML-Dateien enthält, und nicht die spezifische XML-Datei für Ihr Projekt.

  4. Ändern Sie abschließend Areas\HelpPage\XmlDocumentationProviderdie folgenden Methoden:

    ein. Ersetzen Sie das _documentNavigatorFeld durch:

    private List<XPathNavigator> _documentNavigators = new List<XPathNavigator>();

    b. Ersetzen Sie den Konstruktor durch:

    public XmlDocumentationProvider(string appDataPath)
    {
        if (appDataPath == null)
        {
            throw new ArgumentNullException("appDataPath");
        }
    
        var files = new[] { "XmlDocument.xml", "Subproject.xml" };
        foreach (var file in files)
        {
            XPathDocument xpath = new XPathDocument(Path.Combine(appDataPath, file));
            _documentNavigators.Add(xpath.CreateNavigator());
        }
    }

    c. Fügen Sie die folgende Methode unter dem Konstruktor hinzu:

    private XPathNavigator SelectSingleNode(string selectExpression)
    {
        foreach (var navigator in _documentNavigators)
        {
            var propertyNode = navigator.SelectSingleNode(selectExpression);
            if (propertyNode != null)
                return propertyNode;
        }
        return null;
    }

    d. Beheben Sie zuletzt alle Compilerfehler (es sollten drei sein), die zu Verweisen auf _documentNavigator.SelectSingleNodeden _documentNavigator.Teil führen , und entfernen Sie ihn, sodass er jetzt die SelectSingleNodeoben definierte neue Methode aufruft .

Dieser letzte Schritt ändert den Dokumentanbieter so, dass die Suche in mehreren XML-Dokumenten nach dem Hilfetext und nicht nur nach dem primären Projekt unterstützt wird.

Wenn Sie nun Ihre Hilfedokumentation untersuchen, enthält sie XML-Dokumentation von Typen in Ihrem verwandten Projekt.

Kirk Woll
quelle
7
Hervorragende Antwort. Ich denke tatsächlich, dass es für den Konstruktor etwas einfacher ist, ein Array von Zeichenfolgen zu akzeptieren: public XmlDocumentationProvider (Zeichenfolge appDataPath) und diese Liste im Dokumentationsanbieter aufzulisten.
Kapitän John
14
Fantastisch, das war genau das, wonach ich gesucht habe !! Schlagen Sie vor, die var files...Zeile durch zu ersetzen , var files = Directory.GetFiles(documentPath, "*.xml");wenn Sie (wie ich) nicht immer die Namen / Anzahl der XML-Dokumentationsdateien kennen, die dort vorhanden sein werden. Könnte bei Bedarf auch weiter filtern.
3.
2
@Leandro, danke für die Verbesserung der Antwort! :) Ich bin froh, dass du es hilfreich fandest.
Kirk Woll
5
Wenn ich könnte, würde ich Sie für diese detaillierte und korrekte Antwort +10
Mark van Straten
2
Ich möchte meine Modifikationen hier zusätzlich zu einigen anderen hinzufügen. Ich habe die ... \ Notation verwendet, um die XML-Datei im Stammprojekt App_Data \ Dokumentationsordner erstellen zu lassen. Ich habe dann die @ sǝɯɐſ-Methode verwendet, um alle XML-Dateien aus diesem Verzeichnis abzurufen. Das funktioniert wunderbar und ich bin überrascht, dass es nicht einfach so funktioniert. Danke vielmals.
Darroll
32

Ich bin auch darauf gestoßen, wollte aber keinen der generierten Codes bearbeiten oder duplizieren, um später Probleme zu vermeiden.

Aufbauend auf den anderen Antworten finden Sie hier einen eigenständigen Dokumentationsanbieter für mehrere XML-Quellen. Fügen Sie dies einfach in Ihr Projekt ein:

/// <summary>A custom <see cref="IDocumentationProvider"/> that reads the API documentation from a collection of XML documentation files.</summary>
public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
    /*********
    ** Properties
    *********/
    /// <summary>The internal documentation providers for specific files.</summary>
    private readonly XmlDocumentationProvider[] Providers;


    /*********
    ** Public methods
    *********/
    /// <summary>Construct an instance.</summary>
    /// <param name="paths">The physical paths to the XML documents.</param>
    public MultiXmlDocumentationProvider(params string[] paths)
    {
        this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetDocumentation(MemberInfo subject)
    {
        return this.GetFirstMatch(p => p.GetDocumentation(subject));
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetDocumentation(Type subject)
    {
        return this.GetFirstMatch(p => p.GetDocumentation(subject));
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetDocumentation(HttpControllerDescriptor subject)
    {
        return this.GetFirstMatch(p => p.GetDocumentation(subject));
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetDocumentation(HttpActionDescriptor subject)
    {
        return this.GetFirstMatch(p => p.GetDocumentation(subject));
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetDocumentation(HttpParameterDescriptor subject)
    {
        return this.GetFirstMatch(p => p.GetDocumentation(subject));
    }

    /// <summary>Gets the documentation for a subject.</summary>
    /// <param name="subject">The subject to document.</param>
    public string GetResponseDocumentation(HttpActionDescriptor subject)
    {
        return this.GetFirstMatch(p => p.GetResponseDocumentation(subject));
    }


    /*********
    ** Private methods
    *********/
    /// <summary>Get the first valid result from the collection of XML documentation providers.</summary>
    /// <param name="expr">The method to invoke.</param>
    private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
    {
        return this.Providers
            .Select(expr)
            .FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
    }
}

... und aktivieren Sie es in Ihrem HelpPageConfigmit den Pfaden zu den gewünschten XML-Dokumenten:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml")));
Pathoschild
quelle
Dies ist eine großartige Lösung. Ich bevorzuge es gegenüber Lösungen, bei denen die Standard-HelpPage-Klassen geändert werden müssen, da sie bei Updates überschrieben werden.
AronVanAmmers
3
Das funktioniert hervorragend, danke für die Veröffentlichung. Um jedem, der dies verwendet, etwas Zeit zu sparen, müssen Sie noch die ersten beiden Schritte der oben akzeptierten Antwort von kirk ausführen, dh 1) Aktivieren Sie die XML-Dokumentation für Ihr Teilprojekt und 2) Ändern Sie das Postbuild-Ereignis Ihres Web-API-Projekts, in das diese XML-Datei kopiert werden soll Ihren App_Data-Ordner.
TomRedox
1
und dann wird diese Zeile zu: config.SetDocumentationProvider (neuer MultiXmlDocumentationProvider (HttpContext.Current.Server.MapPath) ("~ / App_Data / [XML-Dateiname des ursprünglichen Web-API-Projekts, standardmäßig XmlDocument] .xml"), HttpContext.Curr ("~ / App_Data / [Wie auch immer Sie Ihren SubProject-XML-Dateinamen genannt haben] .xml")));
TomRedox
Befolgen Sie die Schritte, aber es hat nicht funktioniert :(. Es gibt keinen Fehler, also nicht sicher, was schief gelaufen ist. Es zeigt immer noch nur das API-Dokument, aber nicht das zusätzliche Projektdokument. Ich habe auch die Schritte in der akzeptierten Antwort ausprobiert und es ist dasselbe
Was
Aus irgendeinem Grund wird immer noch die Standard-GET-API angezeigt, die mit der Projektvorlage für den Einstieg in VS geliefert wird.
John Zabroski
0

Die einfachste Möglichkeit, dieses Problem zu beheben, besteht darin, den Ordner App_Code auf dem von Ihnen bereitgestellten Server zu erstellen. Kopieren Sie dann die XmlDocument.xml, die Sie in Ihrem bin-Ordner haben, lokal in den App_Code-Ordner

Ziregbe Otee
quelle
Danke für den Vorschlag !! Keine -1 mehr für solch eine hilfreiche Antwort. Ja, wenn Sie es für den Azure Cloud App Service bereitstellen, treten bei der Bereitstellung mehrerer * .xml-Dateien viele Probleme auf. Daher kann es sehr schwierig sein, sie beispielsweise für Swagger verfügbar zu machen. Ich würde jedoch lieber einen anderen serverseitigen Standardordner von ASP.Net wählen, nämlich App_GlobalResources, da xmldoc-Dateien den Ressourcen ziemlich ähnlich sind. Dies gilt insbesondere, weil ich noch keinen App_Code-Ordner in meinem Projekt hatte und es keine Rolle spielte, welcher Standardordner erstellt werden sollte.
Moudrick
Der folgende Standardordner hat für mich funktioniert: App_Code - ist vom Client in den Standardeinstellungen nicht sichtbar App_GlobalResources - ist vom Client in den Standardeinstellungen nicht sichtbar App_LocalResources - ist vom Client in den Standardeinstellungen nicht sichtbar
moudrick
Lassen Sie mich auch die Probleme mit jedem Standardordner auflisten, die bei mir nicht funktioniert haben. bin - nur * .xml für die Hauptbaugruppe wird in App_Data kopiert - die praktischste Einstellung besteht darin, bei der Bereitstellung in der Cloud alles in diesem Ordner zu überspringen
moudrick
Könnte jemand, der interessiert ist, diese Antwort bearbeiten, um all diese Überlegungen zu reflektieren, wahrscheinlich mit erweiterten Spekulationen?
Moudrick