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


102

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

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

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

OpenIdLoginRequest单独的C#项目中定义where ,如下所示:

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文档的子项目中的类型出现在Web API XML文档中?

Answers:


165

没有内置方法可以实现此目的。但是,只需要几个步骤:

  1. 像为Web API项目一样,为子项目(从项目属性/构建)启用XML文档。除了这次,将其直接路由到,XmlDocument.xml以便在项目的根文件夹中生成它。

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

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

    在何处Subproject.xml应将其重命名为您的项目名称为plus .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>();

    b。将构造函数替换为:

    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)并在Documentation provider中枚举此列表。
约翰上尉

14
太棒了,这就是我想要的!如果您(像我一样)不总是知道那里的xml文档文件的名称/数量,建议用替换该var files...var files = Directory.GetFiles(documentPath, "*.xml");。还可以根据需要进行进一步过滤。
sǝɯɐſ 2014年

2
@Leandro,感谢您改善答案!:)很高兴您发现它有所帮助。
Kirk Woll 2014年

5
如果可以的话,我会给您+10,以获取详细而正确的答案
Mark van Straten

2
我想在这里添加其他一些修改。我使用... \表示法在根项目App_Data \ documentation文件夹中创建了xml文件。然后,我使用@ sǝɯɐſ方法从该目录中抽取所有xml文件。它的工作方式非常漂亮,并且感到惊讶的是,这不仅仅是开箱即用的方式。非常感谢。
达尔罗尔

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 2015年

3
这非常出色,谢谢您的发布。为了节省使用该代码的任何人的时间,您仍然需要完成上述kirk可接受的答案的前两个阶段,即1)为子项目启用XML文档,以及2)修改Web API项目的postbuild事件以将此XML文件复制到您的App_Data文件夹。
tomRedox

1
然后此行变为:config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath(“〜/ App_Data / [原始Web API项目的xml文件名,默认为XmlDocument] .xml”),HttpContext.Current.Server.MapPath (“〜/ App_Data / [无论您叫什么子项目xml文件名] .xml”)));
tomRedox

遵循了步骤,但没有成功:(.。没有错误,所以不确定出了什么问题。它仍然只显示api文档,而不显示其他项目文档。我也尝试了接受的答案中的步骤,这是相同的我有什么特别需要检查的吗?
stt106 2015年

由于某种原因,我仍然看到VS中的入门项目模板随附的默认GET api / me。
John Zabroski



0

解决此问题的最简单方法是在部署的服务器上创建App_Code文件夹。然后将本地bin文件夹中的XmlDocument.xml复制到App_Code文件夹中


感谢您的建议!这样的有用答案不再是-1。是的,如果将其部署到Azure Cloud App Service,则多个* .xml部署会发生许多问题,因此,例如,使它们可用于大张旗鼓可能确实很棘手。但是我宁愿选择另一个标准ASP.Net服务器端文件夹,即App_GlobalResources,因为xmldoc文件与资源非常相似。这是特别正确的,因为我的项目中仍然没有App_Code文件夹,创建哪个标准文件夹也没有关系。
moudrick '16

以下标准文件夹为我工作:App_Code-在默认设置下对客户端不可见App_GlobalResources-在默认设置下对客户端不可见App_LocalResources-在默认设置下对客户端不可见
moudrick

我还要列出每个不适用于我的标准文件夹的问题。bin-仅将主程序集的* .xml部署到App_Data-最实用的设置是在部署到云时跳过此文件夹中的所有内容
moudrick

有兴趣的人可以编辑此答案以反映上述所有注意事项,并且可能会有更多的猜测吗?
moudrick '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.