Web Api的Xml文档如何包含主项目之外的文档?

Kir*_*oll 100 c# xml-documentation asp.net-web-api

将XmlDoc集成到Web Api项目中的文档似乎只处理所有API类型都属于WebApi项目的情况.特别是,它讨论了如何将XML文档重新路由到App_Data/XmlDocument.xml取消注释配置中将使用该文件的行.这隐含地只允许一个项目的文档文件.

但是,在我的设置中,我在一个常见的"模型"项目中定义了我的请求和响应类型.这意味着如果我有一个端点,例如:

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

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

尽管有XML文档,但request在查看特定于端点的帮助页面(即http://localhost/Help/Api/POST-auth-openid-login)时,参数的属性不包含任何文档.

如何在Web API XML文档中显示具有XML文档的子项目中的类型?

Kir*_*oll 160

没有内置的方法来实现这一目标.但是,它只需要几个步骤:

  1. 为您的子项目启用XML文档(来自项目属性/构建),就像您对Web API项目一样.除此之外,将其直接路由到XmlDocument.xml它以便在项目的根文件夹中生成它.

  2. 修改Web API项目的postbuild事件,将此XML文件复制到您的App_Data文件夹中:

    copy "$(SolutionDir)SubProject\XmlDocument.xml" "$(ProjectDir)\App_Data\Subproject.xml"
    
    Run Code Online (Sandbox Code Playgroud)

    哪里Subproject.xml应该重命名为你的项目名称加上什么.xml.

  3. 接下来打开Areas\HelpPage\HelpPageConfig并找到以下行:

    config.SetDocumentationProvider(new XmlDocumentationProvider(
        HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
    
    Run Code Online (Sandbox Code Playgroud)

    这是您最初取消注释的行,以便首先启用XML帮助文档.将该行替换为:

    config.SetDocumentationProvider(new XmlDocumentationProvider(
        HttpContext.Current.Server.MapPath("~/App_Data")));
    
    Run Code Online (Sandbox Code Playgroud)

    此步骤确保XmlDocumentationProvider传递包含XML文件的目录,而不是项目的特定XML文件.

  4. 最后,通过Areas\HelpPage\XmlDocumentationProvider以下方式进行修改:

    一个.将_documentNavigator字段替换为:

    private List<XPathNavigator> _documentNavigators = new List<XPathNavigator>();
    
    Run Code Online (Sandbox Code Playgroud)

    湾 用以下内容替换构造函数:

    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());
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    C.在构造函数下面添加以下方法:

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

    d.最后,修复所有编译器错误(应该有三个),导致引用_documentNavigator.SelectSingleNode和删除_documentNavigator.部分,以便它现在调用SelectSingleNode我们上面定义的新方法.

最后一步是修改文档提供程序以支持在多个XML文档中查找帮助文本而不仅仅是主项目.

现在,当您检查帮助文档时,它将包含相关项目中类型的XML文档.

  • 太棒了,这就是我想要的!! 建议用`var files = Directory.GetFiles(documentPath,"*.xml")替换`var files ...`行;`如果你(像我一样)不会总是知道xml文档文件的名称/数量那么将在那里.也可以根据需要进行进一步过滤. (14认同)
  • 很好的答案.我实际上认为构造函数接受一个字符串数组要容易一些:public XmlDocumentationProvider(string appDataPath)并在文档提供程序中枚举此列表. (7认同)
  • 如果可以,我会+10你这个详细和正确的答案 (4认同)
  • @Leandro,谢谢你提高答案!:)很高兴你发现它很有帮助. (2认同)
  • 我想在这里添加其他一些修改。我使用... \表示法在根项目App_Data \ documentation文件夹中创建了xml文件。然后,我使用@ sǝɯɐſ方法从该目录中抽取所有xml文件。它的工作方式非常漂亮,并且感到惊讶的是,这不仅是开箱即用的方式。非常感谢。 (2认同)

Pat*_*ild 31

我也碰到了这个,但我不想编辑或复制任何生成的代码以避免以后出现问题.

在其他答案的基础上,这里是一个包含多个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));
    }
}
Run Code Online (Sandbox Code Playgroud)

...并HelpPageConfig使用所需XML文档的路径启用它:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml")));
Run Code Online (Sandbox Code Playgroud)

  • 这非常出色,感谢您的发布.为了节省使用这个时间的人,你仍然需要完成kirk上面接受的答案的前两个阶段,即1)为你的子项目启用XML文档和2)修改你的Web API项目的postbuild事件,将这个XML文件复制到你的App_Data文件夹. (3认同)

Kir*_*lla 5

另一种简化的方法是合并xml文件.我在下面的回复中的示例代码:

Web Api帮助页面来自多个文件的XML注释