2014-02-19 27 views
90

用于将XmlDoc集成到Web Api项目中的documentation似乎只处理所有API类型都是WebApi项目的一部分的情况。特别是,它讨论了如何将XML文档重新路由到App_Data/XmlDocument.xml,并取消注释将会使用该文件的配置文件中的一行。这隐含地只允许一个项目的文档文件。Web API的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 doccomments的request参数的属性包含当您查看端点特定的帮助页面时(例如http://localhost/Help/Api/POST-auth-openid-login),没有文档。

我该怎么做才能让XML文档的子项目中的类型出现在Web API XML文档中?

回答

141

有没有内置的方式来实现这一点。然而,只需要几个步骤:

  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应该被重新命名为任何你的项目的名称是加.xml

  3. 下一页开放Areas\HelpPage\HelpPageConfig并找到下面的一行:

    ​​

    这是该行你为了使在首位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文档。

+6

优秀的答案。我实际上认为构造函数接受一个字符串数组会更简单一些:public XmlDocumentationProvider(string appDataPath)并枚举文档提供程序中的此列表。 –

+1

@CaptainJohn,谢谢!我同意你的建议会很好。不过,我认为我会保留它,因为它保留了'XmlDocumentationProvider'中的大部分修改。我最初要保持这一行不变,并从传入的文件中提取父文件夹,但对我来说这似乎过于简单。 –

+13

太棒了,这正是我一直在寻找的!建议用'var files = Directory.GetFiles(documentPath,“* .xml”)替换'var files ...'行;'如果你(像我一样)不会知道xml文档文件的名称/数量将在那里。根据需要也可以做进一步的过滤。 –

25

我遇到了这个太,但我不想编辑或复制任何生成的代码以避免以后的问题。

基于其他答案,这里是一个自包含文档提供程序的多个XML源。在HelpPageConfig与路径到你想要的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.GetDocumentation(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)); 
    } 
} 

...并启用它:只需拖放到这个项目中

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml"))); 
+0

这是一个很好的解决方案。我喜欢它了,需要默认HelpPage类的修改,因为他们将在更新被覆盖的解决方案。 – AronVanAmmers

+2

这出色的作品,谢谢你的帖子。要使用此一点时间拯救任何人,你还是需要完成上面kirk接受的答案的前两个阶段,即 1)为您的子项目启用XML文档和 2)修改您的Web API项目的构建后事件以将此XML文件复制到您的App_Data文件夹中。 – tomRedox

+1

然后将此行变成了:config.SetDocumentationProvider(新MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath( “〜/ App_Data文件/ [原创网页API项目的XML文件名,默认的XmlDocument] .XML”),HttpContext.Current.Server。 MapPath(“〜/ App_Data/[无论你称为SubProject xml文件名] .xml”))); – tomRedox

0

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

+0

感谢您的建议!没有更多的-1这种有用的答案。 是的,如果您将它部署到Azure云应用服务,则在部署多个* .xml时会出现许多问题,例如,使它们可用于swagger可能会非常棘手 但我宁愿选择另一个标准的ASP.Net服务器 - 因为xmldoc文件与资源非常相似 特别是tr文件夹,即App_GlobalResources ue,因为我的项目中仍然没有App_Code文件夹,创建哪个标准文件夹并不重要。 – moudrick

+0

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

+0

让我也列出了每个标准文件夹的问题都不适合我。 bin - 主要程序集* .xml被deplopyed到 App_Data - 最实用的设置是跳过此文件夹中部署到云中的所有内容 – moudrick