W jaki sposób dokumentacja XML dla interfejsu API sieci Web może zawierać dokumentację spoza głównego projektu?

102

Dokumentacja umożliwiającym integrację xmldoc do swoich projektów WWW interfejsu API pojawia się tylko do sytuacji, w której uchwyt wszystkich typów API są częścią projektu WebAPI. W szczególności omawia, jak przekierować dokumentację XML do App_Data/XmlDocument.xmli odkomentować wiersz w konfiguracji, który będzie zużywał ten plik. To niejawnie zezwala tylko na jeden plik dokumentacji projektu.

Jednak w mojej konfiguracji typy żądań i odpowiedzi są zdefiniowane we wspólnym projekcie „Modele”. Oznacza to, że jeśli mam zdefiniowany punkt końcowy, taki jak:

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

Where OpenIdLoginRequestjest zdefiniowany w oddzielnym projekcie C #, jak na przykład:

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

    ...
}

Pomimo dokumentów XML, właściwości requestparametru nie zawierają dokumentacji podczas przeglądania strony pomocy specyficznej dla punktu końcowego (tj http://localhost/Help/Api/POST-auth-openid-login.).

Jak sprawić, by typy w podprojektach z dokumentacją XML pojawiały się w dokumentacji XML interfejsu API sieci Web?

Kirk Woll
źródło

Odpowiedzi:

165

Nie ma na to wbudowanej metody. Wymaga to jednak tylko kilku kroków:

  1. Włącz dokumentację XML dla podprojektu (z właściwości projektu / kompilacji), tak jak w przypadku projektu internetowego interfejsu API. Z wyjątkiem tego czasu, przekieruj go bezpośrednio do XmlDocument.xml, aby został wygenerowany w folderze głównym projektu.

  2. Zmodyfikuj zdarzenie postbuild projektu interfejsu API sieci Web, aby skopiować ten plik XML do App_Datafolderu:

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

    Gdzie Subproject.xmlnależy zmienić nazwę na dowolną nazwę projektu plus .xml.

  3. Następnie otwórz Areas\HelpPage\App_Start\HelpPageConfigi znajdź następującą linię:

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

    Jest to wiersz, który początkowo odkomentowałeś, aby w pierwszej kolejności włączyć dokumentację pomocy XML. Zastąp tę linię:

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

    Ten krok zapewnia XmlDocumentationProviderprzekazanie katalogu zawierającego pliki XML zamiast określonego pliku XML projektu.

  4. Na koniec zmodyfikuj Areas\HelpPage\XmlDocumentationProviderw następujący sposób:

    za. Zastąp _documentNavigatorpole:

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

    b. Zamień konstruktora na:

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

    do. Dodaj następującą metodę poniżej konstruktora:

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

    re. I na koniec napraw wszystkie błędy kompilatora (powinny być trzy) powodujące odwołania do _documentNavigator.SelectSingleNodei usuń _documentNavigator.część, aby teraz wywoływała nową SelectSingleNodemetodę, którą zdefiniowaliśmy powyżej.

Ten ostatni krok modyfikuje dostawcę dokumentów do obsługi wyszukiwania tekstu pomocy w wielu dokumentach XML, a nie tylko w projekcie głównym.

Teraz, gdy przejrzysz dokumentację Pomocy, będzie ona zawierała dokumentację XML z typów w powiązanym projekcie.

Kirk Woll
źródło
7
Doskonała odpowiedź. Myślę, że konstruktorowi trochę łatwiej jest zaakceptować tablicę ciągów: public XmlDocumentationProvider (string appDataPath) i wyliczyć tę listę w dostawcy dokumentacji.
Kapitan John,
14
Fantastycznie, właśnie tego szukałem !! Zasugeruj zastąpienie var files...wiersza var files = Directory.GetFiles(documentPath, "*.xml");jeśli (tak jak ja) nie zawsze będziesz znać nazwy / liczbę plików dokumentacji xml, które tam będą. W razie potrzeby można też przeprowadzić dalsze filtrowanie.
s
2
@Leandro, dzięki za poprawienie odpowiedzi! :) Cieszę się, że okazało się to pomocne.
Kirk Woll
5
Gdybym mógł, dałbym +10 za tę szczegółową i poprawną odpowiedź
Mark van Straten
2
Chciałbym dodać moje modyfikacje do niektórych innych tutaj. Użyłem notacji ... \ do utworzenia pliku xml w folderze głównym projektu App_Data \ Documentation. Następnie użyłem metody @ sǝɯɐſ do pulowania wszystkich plików xml z tego katalogu. Działa to pięknie i jestem zaskoczony, że nie tylko tak działa po wyjęciu z pudełka. Wielkie dzięki.
Darroll
32

Też napotkałem to, ale nie chciałem edytować ani powielać żadnego z wygenerowanego kodu, aby później uniknąć problemów.

Opierając się na innych odpowiedziach, oto niezależny dostawca dokumentacji dla wielu źródeł XML. Po prostu wrzuć to do swojego projektu:

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

... i włącz go w swoim HelpPageConfigze ścieżkami do żądanych dokumentów XML:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml")));
Pathoschild
źródło
To świetne rozwiązanie. Wolę to od rozwiązań, które wymagają modyfikacji domyślnych klas HelpPage, ponieważ będą nadpisywane przy aktualizacjach.
AronVanAmmers
3
To działa wspaniale, dziękuję za wysłanie. Aby zaoszczędzić każdemu trochę czasu przy użyciu tego, nadal musisz wykonać pierwsze dwa etapy powyższej zaakceptowanej odpowiedzi kirk, tj. 1) Włącz dokumentację XML dla swojego podprojektu i 2) Zmodyfikuj zdarzenie postbuild swojego projektu Web API, aby skopiować ten plik XML do Twój folder App_Data.
tomRedox
1
a następnie ta linia staje się: config.SetDocumentationProvider (new MultiXmlDocumentationProvider (HttpContext.Current.Server.MapPath ("~ / App_Data / [nazwa pliku xml oryginalnego projektu interfejsu API sieci Web, domyślnie XmlDocument] .xml"), HttpContext.Current.Server.M ("~ / App_Data / [Jakkolwiek nazwałeś swój plik SubProject xml] .xml")));
tomRedox
Wykonałem te kroki, ale nie zadziałało :(. Nie ma błędu, więc nie jestem pewien, co poszło nie tak. Nadal pokazuje tylko dokument API, ale nie dodatkowy dokument projektu. Próbowałem też kroków w zaakceptowanej odpowiedzi i to samo
.Czy
Z jakiegoś powodu nadal widzę domyślny interfejs API / Me GET, który jest dostarczany z szablonem projektu wprowadzającego w VS.
John Zabroski
0

Najłatwiejszym sposobem rozwiązania tego problemu jest utworzenie folderu App_Code na wdrożonym serwerze. Następnie skopiuj XmlDocument.xml, który masz w folderze bin lokalnie do folderu App_Code

Ziregbe Otee
źródło
Dzięki za sugestię !! Nie więcej -1 za tak pomocną odpowiedź. Tak, jeśli wdrażasz go w usłudze Azure Cloud App Service, wiele problemów występuje przy wdrażaniu wielu plików * .xml, więc udostępnienie ich na przykład w celu przechwycenia może być naprawdę trudne. Ale wolałbym wybrać inny standardowy folder po stronie serwera ASP.Net, a mianowicie App_GlobalResources, ponieważ pliki xmldoc są bardzo podobne do zasobów. Jest to szczególnie ważne, ponieważ nadal nie miałem folderu App_Code w moim projekcie i nie miało znaczenia, który standardowy folder utworzyć.
moudrick
Działał dla mnie następujący standardowy folder: App_Code - nie jest widoczny z klienta przy ustawieniach domyślnych App_GlobalResources - nie jest widoczny z klienta przy ustawieniach domyślnych App_LocalResources - nie jest widoczny z klienta przy ustawieniach domyślnych
moudrick
Pozwólcie, że wymienię również problemy z każdym ze standardowych folderów, które nie działały dla mnie. bin - tylko * .xml dla głównego zestawu jest usuwany do App_Data - najbardziej praktycznym ustawieniem jest pominięcie wszystkiego w tym folderze podczas wdrażania w chmurze
moudrick
Czy ktokolwiek zainteresowany mógłby edytować tę odpowiedź, aby odzwierciedlić wszystkie powyższe rozważania, prawdopodobnie z rozszerzonymi spekulacjami?
moudrick