Как может документация Xml для веб-API включать документацию, выходящую за рамки основного проекта?

102

Документация для обеспечения интеграции XmlDoc в ваши Api проектов Web появляется только рукоятки ситуаций , когда все ваши типов API являются частью вашего WebAPI проекта. В частности, обсуждается, как перенаправить XML-документацию App_Data/XmlDocument.xmlи раскомментировать строку в вашей конфигурации, которая будет использовать этот файл. Это неявно разрешает только один файл документации проекта.

Однако в моих настройках типы запросов и ответов определены в общем проекте «Модели». Это означает, что если у меня определена конечная точка, например:

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

Где OpenIdLoginRequestопределено в отдельном проекте C #, например:

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

    ...
}

Несмотря на комментарии XML, свойства requestпараметра не содержат документации при просмотре страницы справки для конкретной конечной точки (т.е. http://localhost/Help/Api/POST-auth-openid-login).

Как я могу сделать так, чтобы типы в подпроектах с XML-документацией отображались в XML-документации Web API?

Кирк Уолл
источник

Ответы:

165

Для этого нет встроенного способа. Однако для этого требуется всего несколько шагов:

  1. Включите XML-документацию для своего подпроекта (из свойств / сборки проекта), как для проекта веб-API. За исключением этого раза, направьте его напрямую, чтобы XmlDocument.xmlон был создан в корневой папке вашего проекта.

  2. Измените событие postbuild вашего проекта Web API, чтобы скопировать этот XML-файл в вашу App_Dataпапку:

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

    Где Subproject.xmlследует переименовать в любое название вашего проекта плюс .xml.

  3. Затем откройте Areas\HelpPage\App_Start\HelpPageConfigи найдите следующую строку:

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

    Это строка, которую вы изначально раскомментировали, чтобы в первую очередь включить справочную документацию XML. Замените эту строку на:

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

    Этот шаг гарантирует, что XmlDocumentationProviderпередается каталог, содержащий ваши XML-файлы, а не конкретный XML-файл для вашего проекта.

  4. Наконец, измените Areas\HelpPage\XmlDocumentationProviderследующим образом:

    а. Замените _documentNavigatorполе на:

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

    б. Замените конструктор на:

    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. Добавьте следующий метод под конструктором:

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

    d. И, наконец, исправьте все ошибки компилятора (их должно быть три), приводящие к ссылкам _documentNavigator.SelectSingleNodeи удалите _documentNavigator.часть, чтобы теперь она вызывала новый SelectSingleNodeметод, который мы определили выше.

Этот последний шаг - это то, что модифицирует поставщика документов для поддержки поиска текста справки в нескольких XML-документах, а не только в основном проекте.

Теперь, когда вы изучите свою справочную документацию, она будет включать XML-документацию по типам из вашего связанного проекта.

Кирк Уолл
источник
7
Отличный ответ. На самом деле я думаю, что конструктору немного легче принять массив строк: public XmlDocumentationProvider (string appDataPath) и перечислить этот список в поставщике документации.
Капитан Джон
14
Потрясающе, это было именно то, что я искал !! Предложите заменить var files...строку на, var files = Directory.GetFiles(documentPath, "*.xml");если вы (как и я) не всегда будете знать имена / количество файлов документации xml, которые будут там. При необходимости может также выполнять дополнительную фильтрацию.
sǝɯɐſ
2
@Leandro, спасибо за улучшение ответа! :) Рад, что ты нашел это полезным.
Кирк Уолл
5
Если бы я мог, я бы поставил вам +10 за этот подробный и правильный ответ
Марк ван Стратен,
2
Я хотел бы добавить сюда свои модификации поверх некоторых других. Я использовал нотацию ... \, чтобы создать XML-файл в корневой папке проекта App_Data \ documentation. Затем я использовал метод @ sǝɯɐſ для удаления всех XML-файлов из этого каталога. Это прекрасно работает, и я удивлен, что это работает не только из коробки. Большое спасибо.
Darroll
32

Я тоже столкнулся с этим, но я не хотел редактировать или дублировать какой-либо сгенерированный код, чтобы избежать проблем позже.

Основываясь на других ответах, вот автономный поставщик документации для нескольких источников XML. Просто вставьте это в свой проект:

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

... и включите его в себе, HelpPageConfigуказав пути к нужным XML-документам:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml")));
Пафошильд
источник
Это отличное решение. Я предпочитаю его решениям, требующим изменения классов HelpPage по умолчанию, поскольку они будут перезаписаны при обновлении.
AronVanAmmers
3
Это прекрасно работает, спасибо за публикацию. Чтобы сэкономить кому-то, кто использует это немного времени, вам все равно нужно выполнить первые два этапа принятого ответа Кирка выше, то есть 1) Включить документацию XML для вашего подпроекта и 2) Изменить событие postbuild вашего проекта веб-API, чтобы скопировать этот XML-файл в ваша папка App_Data.
tomRedox 01
1
а затем эта строка становится: config.SetDocumentationProvider (new MultiXmlDocumentationProvider (HttpContext.Current.Server.MapPath ("~ / App_Data / [имя файла xml исходного веб-проекта api, по умолчанию XmlDocument] .xml"), HttpContext.Current.Server.MapPath ("~ / App_Data / [Как бы вы ни называли свой субпроект xml filename] .xml")));
tomRedox 01
Выполнил шаги, но не сработал :(. Ошибки нет, поэтому не уверен, что пошло не так. Он по-прежнему показывает только документ api, но не дополнительный документ проекта. Я также пробовал шаги в принятом ответе, и это то же самое . Что-нибудь особенное, что я должен проверить?
stt106
По какой-то причине я до сих пор вижу GET api / me по умолчанию, который поставляется с шаблоном проекта для начала работы в VS.
Джон Заброски
0

Самый простой способ решить эту проблему - создать папку App_Code на развернутом сервере. Затем скопируйте XmlDocument.xml, который находится в папке bin, локально в папку App_Code.

Зирегбе Оти
источник
Спасибо за предложение !! Не больше -1 за такой полезный ответ. Да, если вы развертываете его в Azure Cloud App Service, многие проблемы возникают при развертывании нескольких * .xml, поэтому, например, сделать их доступными для чванства может быть очень сложно. Но я бы предпочел выбрать другую стандартную папку на стороне сервера ASP.Net, а именно App_GlobalResources, поскольку файлы xmldoc во многом похожи на ресурсы. Это особенно верно, потому что в моем проекте все еще не было папки App_Code, и не имело значения, какую стандартную папку создать.
moudrick
У меня работала следующая стандартная папка: App_Code - не отображается на клиенте в настройках по умолчанию App_GlobalResources - не отображается из клиента в настройках по умолчанию App_LocalResources - не отображается из клиента в настройках по умолчанию
moudrick
Позвольте мне также перечислить проблемы с каждой из стандартных папок, которые у меня не работали. bin - только * .xml для основной сборки деплопируется в App_Data - наиболее практичный параметр - пропустить все в этой папке при развертывании в облако
Moudrick
Может ли кто-нибудь, кто заинтересован, отредактировать этот ответ, чтобы отразить все эти соображения, приведенные выше, возможно, с расширенными предположениями?
moudrick